From b05f4976696231e7c93088bade7cc0439d9d01fe Mon Sep 17 00:00:00 2001 From: Stefan Heimes Date: Tue, 18 Oct 2022 08:35:16 +0200 Subject: [PATCH 01/53] Correct the composer-require-checker.json - Remove a unused "c" at the end of the file. --- .composer-require-checker.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.composer-require-checker.json b/.composer-require-checker.json index ae408843..238bb3ab 100644 --- a/.composer-require-checker.json +++ b/.composer-require-checker.json @@ -13,4 +13,4 @@ "Contao\\NewsArchiveModel", "Contao\\NewsModel" ] -}c \ No newline at end of file +} \ No newline at end of file From a7cde9506d88cbe0cdfd741ecf4f117bfb81354a Mon Sep 17 00:00:00 2001 From: Stefan Heimes Date: Tue, 18 Oct 2022 08:46:15 +0200 Subject: [PATCH 02/53] Remove req-dev elements like phpmd --- composer.json | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/composer.json b/composer.json index db483c24..b352060d 100644 --- a/composer.json +++ b/composer.json @@ -49,13 +49,10 @@ }, "require-dev": { "contao/manager-plugin": "^2.8", - "friendsofphp/php-cs-fixer": "^2.13", "friendsofsymfony/http-cache": "^2.9", "menatwork/contao-multicolumnwizard-bundle": "^3.4", "php-http/guzzle6-adapter": "^2.0", - "phpcq/all-tasks": "^1.3", - "phpmd/phpmd": "^2.8", - "phpunit/phpunit": "^7.5 || ^8.5 || ^9.3" + "phpcq/all-tasks": "^1.3" }, "conflict": { "menatwork/contao-multicolumnwizard-bundle": "<3.4.9" From 2601303ea2550bd3860ebb3afa71f214db50c739 Mon Sep 17 00:00:00 2001 From: Christian Schiffler Date: Tue, 25 Oct 2022 18:59:37 +0200 Subject: [PATCH 03/53] Bump core bundle --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index b352060d..5b930465 100644 --- a/composer.json +++ b/composer.json @@ -38,7 +38,7 @@ "contao-community-alliance/events-contao-bindings": "^4.13", "contao-community-alliance/translator": "^2.3", "contao-community-alliance/url-builder": "^1.3", - "contao/core-bundle": "^4.9", + "contao/core-bundle": "^4.13", "doctrine/cache": "^1.13 || ^2.1", "psr/event-dispatcher": "^1.0", "symfony/cache": "4.4.* || ^5.4", From b51b15e9b751ab6cbeb2b8e4b3b9c074d7bd8631 Mon Sep 17 00:00:00 2001 From: Christian Schiffler Date: Tue, 25 Oct 2022 18:59:52 +0200 Subject: [PATCH 04/53] Switch to phpcq2 runner from all-tasks --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 5b930465..27a1cd6d 100644 --- a/composer.json +++ b/composer.json @@ -52,7 +52,7 @@ "friendsofsymfony/http-cache": "^2.9", "menatwork/contao-multicolumnwizard-bundle": "^3.4", "php-http/guzzle6-adapter": "^2.0", - "phpcq/all-tasks": "^1.3" + "phpcq/runner-bootstrap": "^1.0@dev" }, "conflict": { "menatwork/contao-multicolumnwizard-bundle": "<3.4.9" From be916f4a92882dde5602f0e2e6b4d6a36f051544 Mon Sep 17 00:00:00 2001 From: Christian Schiffler Date: Tue, 25 Oct 2022 19:04:21 +0200 Subject: [PATCH 05/53] Remove forgotten build-default.properties --- build.default.properties | 28 ---------------------------- 1 file changed, 28 deletions(-) delete mode 100644 build.default.properties diff --git a/build.default.properties b/build.default.properties deleted file mode 100644 index 558d96f2..00000000 --- a/build.default.properties +++ /dev/null @@ -1,28 +0,0 @@ -##################################################### -## This project is using the ## -## PHP code quality project (phpcq) ## -## ## -## https://github.com/phpcq/phpcq ## -##################################################### - -phpcs.standard=${basedir}/vendor/phpcq/coding-standard/phpcs/PhpCodeQuality/ruleset.xml -phpmd.ruleset=${basedir}/vendor/phpcq/coding-standard/phpmd/ruleset.xml - -# Note: the pathes are calculated relative to the source path. -# Therefore: src/Foo => Foo -# NO leading slash! -phpcpd.excluded=DataDefinition/Palette/Builder/Event \ - DataDefinition/Palette/Condition \ - Contao/Callback \ - Contao/View/Contao2BackendView/Event \ - Event \ - -# Excluded public sources from phpcs. -phpcs.excluded=deprecated-autoload.php\ - ,src/Resources/public/*\ - ,src/Resources/contao/* - -autoload-validation.customflags=--add-autoloader=autoload-validation-hack.php - -# Excluded the legacy parser from phpmd -phpmd.excluded=*/Contao/Dca/Builder/Legacy/*,*/Contao/Dca/Palette/Legacy* From aff75691416ddcdea9efbefef35b75bfa132dd4c Mon Sep 17 00:00:00 2001 From: Christian Schiffler Date: Tue, 25 Oct 2022 19:04:41 +0200 Subject: [PATCH 06/53] Add .phpcq dir to gitignore --- .gitignore | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index 2c7bef59..8e9fb920 100644 --- a/.gitignore +++ b/.gitignore @@ -2,7 +2,7 @@ .DS_Store Thumbs.db -# IDEs +# IDEs .buildpath .project .settings/ @@ -22,5 +22,4 @@ build/ # PHPCQ and related tools .php_cs.cache -build.properties -.build +.phpcq/* From 20f9331f38b82f14169914fd21e6dd36a259c2df Mon Sep 17 00:00:00 2001 From: Christian Schiffler Date: Tue, 25 Oct 2022 19:15:57 +0200 Subject: [PATCH 07/53] Relax psalm error level to 8 for the moment This is the most relaxed level but we have a shitload of warnings in the current code base. Let's move incremental. --- psalm.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/psalm.xml b/psalm.xml index 9dd9aef2..96ba5246 100644 --- a/psalm.xml +++ b/psalm.xml @@ -1,6 +1,6 @@ - \ No newline at end of file + From d5eb51b10869a1600b149e925e93d18f51d87e02 Mon Sep 17 00:00:00 2001 From: Christian Schiffler Date: Tue, 25 Oct 2022 19:26:36 +0200 Subject: [PATCH 08/53] Ignore vendor files in psalm for the moment --- psalm.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/psalm.xml b/psalm.xml index 96ba5246..e90bf636 100644 --- a/psalm.xml +++ b/psalm.xml @@ -10,6 +10,8 @@ + + From f737413f3c402d7ad48ab6ff5fc5f7c93b3bf231 Mon Sep 17 00:00:00 2001 From: Christian Schiffler Date: Mon, 31 Oct 2022 16:47:19 +0100 Subject: [PATCH 09/53] Fix phpmd issues --- ...ExtendedLegacyDcaDataDefinitionBuilder.php | 5 + .../Legacy/LegacyDcaDataDefinitionBuilder.php | 16 + .../Dca/Palette/LegacyPalettesParser.php | 5 + src/Contao/Event/Subscriber.php | 3 + .../AbstractListShowAllHandler.php | 3 + .../ActionHandler/CopyHandler.php | 6 +- .../ActionHandler/DeleteHandler.php | 2 + .../ActionHandler/EditHandler.php | 2 +- .../ActionHandler/ListViewShowAllHandler.php | 10 +- .../MultipleHandler/EditAllHandler.php | 22 +- .../MultipleHandler/OverrideAllHandler.php | 2 + .../SelectPropertyAllHandler.php | 2 + .../ParentedListViewShowAllHandler.php | 7 +- .../ActionHandler/SelectHandler.php | 1 + .../ActionHandler/ShowHandler.php | 2 + .../ActionHandler/ToggleHandler.php | 2 + .../View/Contao2BackendView/BaseView.php | 2 + .../Contao2BackendView/ButtonRenderer.php | 7 +- .../ContaoWidgetManager.php | 8 +- .../Controller/ClipboardController.php | 8 +- .../View/Contao2BackendView/EditMask.php | 1 + .../CreateModelButtonListener.php | 4 +- .../View/Contao2BackendView/FileSelect.php | 2 + .../View/Contao2BackendView/PanelBuilder.php | 2 + .../Subscriber/WidgetBuilder.php | 3 + .../View/Contao2BackendView/TreePicker.php | 3 + .../View/Contao2BackendView/TreeSelect.php | 2 + .../View/Contao2BackendView/TreeView.php | 7 +- .../View/Contao2BackendView/ViewHelpers.php | 10 +- .../Contao2BackendView/Widget/FileTree.php | 3 + src/Controller/BackendTreeController.php | 2 + src/Controller/DefaultController.php | 5 + src/Controller/ModelCollector.php | 3 + src/DC/General.php | 1 + src/Data/DefaultCollection.php | 2 + src/Data/DefaultDataProvider.php | 2061 +++++++++-------- .../Definition/Properties/DefaultProperty.php | 2 + .../Definition/View/DefaultListingConfig.php | 2 + .../ParentChildCondition.php | 2 + .../Palette/Builder/PaletteBuilder.php | 2 + src/DefaultEnvironment.php | 2 + src/Factory/DcGeneralFactory.php | 2 + ...AbstractPropertyOverrideEditAllHandler.php | 33 +- .../AbstractPropertyVisibilityHandler.php | 7 + tests/BaseConfigRegistryTest.php | 7 + tests/Cache/Http/InvalidateCacheTagsTest.php | 4 + tests/Clipboard/FilterTest.php | 3 + tests/Clipboard/MockedAbstractItem.php | 2 + .../Callback/AbstractCallbackListenerTest.php | 1 + .../AbstractContainerCallbackListenerTest.php | 3 + ...tReturningPropertyCallbackListenerTest.php | 4 + tests/Contao/Event/SubscriberTest.php | 5 + .../Subscriber/CheckPermissionTest.php | 2 + tests/Controller/ModelCollectorTest.php | 9 +- tests/Data/DefaultDataProviderTest.php | 2 + tests/DcGeneralTest.php | 8 +- tests/DefaultEnvironmentTest.php | 2 + tests/Factory/DcGeneralFactoryTest.php | 6 +- tests/Fixtures/Contao/Config.php | 2 + tests/TestCase.php | 3 + 60 files changed, 1251 insertions(+), 1090 deletions(-) diff --git a/src/Contao/Dca/Builder/Legacy/ExtendedLegacyDcaDataDefinitionBuilder.php b/src/Contao/Dca/Builder/Legacy/ExtendedLegacyDcaDataDefinitionBuilder.php index e4909b8a..fde58edd 100644 --- a/src/Contao/Dca/Builder/Legacy/ExtendedLegacyDcaDataDefinitionBuilder.php +++ b/src/Contao/Dca/Builder/Legacy/ExtendedLegacyDcaDataDefinitionBuilder.php @@ -50,6 +50,9 @@ /** * Build the container config from legacy DCA syntax. + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) */ class ExtendedLegacyDcaDataDefinitionBuilder extends DcaReadingDataDefinitionBuilder { @@ -206,6 +209,8 @@ protected function isSpecialName($name) * container. * * @return ContaoDataProviderInformation|null + * + * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ protected function parseSingleDataProvider( ContainerInterface $container, diff --git a/src/Contao/Dca/Builder/Legacy/LegacyDcaDataDefinitionBuilder.php b/src/Contao/Dca/Builder/Legacy/LegacyDcaDataDefinitionBuilder.php index b2008864..9100c59a 100644 --- a/src/Contao/Dca/Builder/Legacy/LegacyDcaDataDefinitionBuilder.php +++ b/src/Contao/Dca/Builder/Legacy/LegacyDcaDataDefinitionBuilder.php @@ -113,6 +113,11 @@ /** * Build the container config from legacy DCA syntax. + * + * @SuppressWarnings(PHPMD.ExcessiveClassLength) + * @SuppressWarnings(PHPMD.TooManyMethods) + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) */ class LegacyDcaDataDefinitionBuilder extends DcaReadingDataDefinitionBuilder { @@ -239,6 +244,9 @@ protected function parsePropertyCallbacks(ContainerInterface $container, EventDi * @param EventDispatcherInterface $dispatcher The event dispatcher in use. * * @return void + * + * @SuppressWarnings(PHPMD.CyclomaticComplexity) + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ protected function parseCallbacks(ContainerInterface $container, EventDispatcherInterface $dispatcher) { @@ -469,6 +477,9 @@ protected function parseBasicDefinition(ContainerInterface $container) * @param ContainerInterface $container The container where the data shall be stored. * * @return void + * + * @SuppressWarnings(PHPMD.CyclomaticComplexity) + * @SuppressWarnings(PHPMD.NPathComplexity) */ protected function parseDataProvider(ContainerInterface $container) { @@ -831,6 +842,9 @@ protected function parsePropertySortingAndGrouping($propName, $propInfo, $defini * @return array * * @throws DcGeneralRuntimeException In case unsupported values are encountered. + * + * @SuppressWarnings(PHPMD.CyclomaticComplexity) + * @SuppressWarnings(PHPMD.NPathComplexity) */ protected function parseListSorting(ListingConfigInterface $listing, array $listDca) { @@ -1507,6 +1521,8 @@ protected function createCommandInstance($commandName, array &$commandDca) * @param array $commandDca The chunk from the DCA containing the command specification. * * @return CommandInterface + * + * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ protected function createCommand($commandName, array $commandDca) { diff --git a/src/Contao/Dca/Palette/LegacyPalettesParser.php b/src/Contao/Dca/Palette/LegacyPalettesParser.php index 91375200..8e8d2257 100644 --- a/src/Contao/Dca/Palette/LegacyPalettesParser.php +++ b/src/Contao/Dca/Palette/LegacyPalettesParser.php @@ -44,6 +44,9 @@ * Class LegacyPalettesParser. * * This class parses the palettes from a legacy DCA into the palette collection definitions being used in DcGeneral. + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) */ class LegacyPalettesParser { @@ -143,6 +146,8 @@ public function parsePalettes( * @param PaletteInterface $palette The palette to be populated [optional]. * * @return Palette + * + * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ public function parsePalette( $paletteSelector, diff --git a/src/Contao/Event/Subscriber.php b/src/Contao/Event/Subscriber.php index d641d46d..7afa18ee 100644 --- a/src/Contao/Event/Subscriber.php +++ b/src/Contao/Event/Subscriber.php @@ -55,6 +55,9 @@ /** * Class Subscriber - gateway to the legacy Contao HOOK style callbacks. + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) */ class Subscriber implements EventSubscriberInterface { diff --git a/src/Contao/View/Contao2BackendView/ActionHandler/AbstractListShowAllHandler.php b/src/Contao/View/Contao2BackendView/ActionHandler/AbstractListShowAllHandler.php index b1dfe075..80428ef6 100644 --- a/src/Contao/View/Contao2BackendView/ActionHandler/AbstractListShowAllHandler.php +++ b/src/Contao/View/Contao2BackendView/ActionHandler/AbstractListShowAllHandler.php @@ -63,6 +63,9 @@ /** * This class is the abstract base for parent list and plain list "showAll" commands. + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) */ abstract class AbstractListShowAllHandler { diff --git a/src/Contao/View/Contao2BackendView/ActionHandler/CopyHandler.php b/src/Contao/View/Contao2BackendView/ActionHandler/CopyHandler.php index c881dcfc..6aa2be29 100644 --- a/src/Contao/View/Contao2BackendView/ActionHandler/CopyHandler.php +++ b/src/Contao/View/Contao2BackendView/ActionHandler/CopyHandler.php @@ -45,6 +45,8 @@ /** * Class CopyModelController handles copy action on a model. + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class CopyHandler { @@ -227,8 +229,8 @@ protected function process(EnvironmentInterface $environment) } // Manual sorting mode. The ClipboardController should pick it up. - $manualSortingProperty = ViewHelpers::getManualSortingProperty($environment); - if ($manualSortingProperty && $environment->getDataProvider()->fieldExists($manualSortingProperty)) { + $manualSorting = ViewHelpers::getManualSortingProperty($environment); + if ($manualSorting && $environment->getDataProvider()->fieldExists($manualSorting)) { return false; } diff --git a/src/Contao/View/Contao2BackendView/ActionHandler/DeleteHandler.php b/src/Contao/View/Contao2BackendView/ActionHandler/DeleteHandler.php index f053a32b..fb2edfb2 100644 --- a/src/Contao/View/Contao2BackendView/ActionHandler/DeleteHandler.php +++ b/src/Contao/View/Contao2BackendView/ActionHandler/DeleteHandler.php @@ -46,6 +46,8 @@ /** * Class DeleteHandler handles the delete action. + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class DeleteHandler { diff --git a/src/Contao/View/Contao2BackendView/ActionHandler/EditHandler.php b/src/Contao/View/Contao2BackendView/ActionHandler/EditHandler.php index 14b6738b..7b51ca93 100644 --- a/src/Contao/View/Contao2BackendView/ActionHandler/EditHandler.php +++ b/src/Contao/View/Contao2BackendView/ActionHandler/EditHandler.php @@ -40,7 +40,7 @@ /** * Class CreateHandler * - * @package ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\ActionHandler + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class EditHandler { diff --git a/src/Contao/View/Contao2BackendView/ActionHandler/ListViewShowAllHandler.php b/src/Contao/View/Contao2BackendView/ActionHandler/ListViewShowAllHandler.php index 7c321d72..c6fc89a5 100644 --- a/src/Contao/View/Contao2BackendView/ActionHandler/ListViewShowAllHandler.php +++ b/src/Contao/View/Contao2BackendView/ActionHandler/ListViewShowAllHandler.php @@ -64,16 +64,16 @@ protected function determineTemplate($groupingInformation) */ protected function renderTemplate(ContaoBackendViewTemplate $template, EnvironmentInterface $environment) { - $dataDefinition = $environment->getDataDefinition(); - $viewDefinition = $dataDefinition->getDefinition(Contao2BackendViewDefinitionInterface::NAME); - $groupAndSortingDefinition = $viewDefinition->getListingConfig()->getGroupAndSortingDefinition(); + $dataDefinition = $environment->getDataDefinition(); + $viewDefinition = $dataDefinition->getDefinition(Contao2BackendViewDefinitionInterface::NAME); + $groupAndSorting = $viewDefinition->getListingConfig()->getGroupAndSortingDefinition(); - $pasteButton = $this->renderPasteTopButton($environment, $groupAndSortingDefinition); + $pasteButton = $this->renderPasteTopButton($environment, $groupAndSorting); parent::renderTemplate($template, $environment); $template ->set('header', $pasteButton ? $this->getEmptyHeader() : null) - ->set('headerButtons', $this->renderPasteTopButton($environment, $groupAndSortingDefinition)); + ->set('headerButtons', $this->renderPasteTopButton($environment, $groupAndSorting)); } /** diff --git a/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/EditAllHandler.php b/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/EditAllHandler.php index 59e9445e..94991131 100644 --- a/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/EditAllHandler.php +++ b/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/EditAllHandler.php @@ -39,6 +39,8 @@ /** * The class handle the "editAll" commands. + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class EditAllHandler extends AbstractPropertyOverrideEditAllHandler { @@ -127,16 +129,16 @@ private function buildFieldSets(Action $action, \ArrayObject $renderInformation, $model = $collection->shift(); $modelId = ModelId::fromModel($model); - $editPropertyValuesBag = $this->getPropertyValueBagFromModel($action, $model, $environment); + $propertyValuesBag = $this->getPropertyValueBagFromModel($action, $model, $environment); if ($formInputs) { - $this->handleEditCollection($action, $editPropertyValuesBag, $model, $renderInformation, $environment); + $this->handleEditCollection($action, $propertyValuesBag, $model, $renderInformation, $environment); } $fields = $this->renderEditFields( $action, new ContaoWidgetManager($environment, $model), $model, - $editPropertyValuesBag, + $propertyValuesBag, $environment ); @@ -399,17 +401,17 @@ private function markModelErrors( /** * Handle edit collection of models. * - * @param Action $action The action. - * @param PropertyValueBagInterface $editPropertyValuesBag The property values. - * @param ModelInterface $model The model. - * @param \ArrayObject $renderInformation The render information. - * @param EnvironmentInterface $environment The environment. + * @param Action $action The action. + * @param PropertyValueBagInterface $propertyValuesBag The property values. + * @param ModelInterface $model The model. + * @param \ArrayObject $renderInformation The render information. + * @param EnvironmentInterface $environment The environment. * * @return void */ private function handleEditCollection( Action $action, - PropertyValueBagInterface $editPropertyValuesBag, + PropertyValueBagInterface $propertyValuesBag, ModelInterface $model, \ArrayObject $renderInformation, EnvironmentInterface $environment @@ -424,7 +426,7 @@ private function handleEditCollection( $revertModel->setId($model->getId()); $revertCollection->push($model); - $this->editCollection($action, $editCollection, $editPropertyValuesBag, $renderInformation, $environment); + $this->editCollection($action, $editCollection, $propertyValuesBag, $renderInformation, $environment); $this->revertValuesByErrors($action, $revertCollection, $environment); } diff --git a/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/OverrideAllHandler.php b/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/OverrideAllHandler.php index a467cc09..9e16edbe 100644 --- a/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/OverrideAllHandler.php +++ b/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/OverrideAllHandler.php @@ -133,6 +133,8 @@ private function process(Action $action, EnvironmentInterface $environment) * @return void * * @deprecated Deprecated since 2.1 and where remove in 3.0. + * + * @SuppressWarnings(PHPMD.LongVariable) */ protected function handleInvalidPropertyValueBag( PropertyValueBagInterface $propertyValueBag = null, diff --git a/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/SelectPropertyAllHandler.php b/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/SelectPropertyAllHandler.php index 319e5a7d..e9a16f31 100644 --- a/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/SelectPropertyAllHandler.php +++ b/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/SelectPropertyAllHandler.php @@ -41,6 +41,8 @@ /** * This class handles the rendering of list view "showAllProperties" actions. + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class SelectPropertyAllHandler extends AbstractListShowAllHandler { diff --git a/src/Contao/View/Contao2BackendView/ActionHandler/ParentedListViewShowAllHandler.php b/src/Contao/View/Contao2BackendView/ActionHandler/ParentedListViewShowAllHandler.php index 406d4c83..dcba34e5 100644 --- a/src/Contao/View/Contao2BackendView/ActionHandler/ParentedListViewShowAllHandler.php +++ b/src/Contao/View/Contao2BackendView/ActionHandler/ParentedListViewShowAllHandler.php @@ -50,6 +50,9 @@ /** * This class handles the rendering of parented list view "showAll" actions. + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) */ class ParentedListViewShowAllHandler extends AbstractListShowAllHandler { @@ -441,8 +444,8 @@ private function getHeaderPasteNewButton(ModelInterface $parentModel, Environmen $filter = new Filter(); $filter->andModelIsFromProvider($basicDefinition->getDataProvider()); - if ($parentDataProviderName = $basicDefinition->getParentDataProvider()) { - $filter->andParentIsFromProvider($parentDataProviderName); + if ($parentProviderName = $basicDefinition->getParentDataProvider()) { + $filter->andParentIsFromProvider($parentProviderName); } else { $filter->andHasNoParent(); } diff --git a/src/Contao/View/Contao2BackendView/ActionHandler/SelectHandler.php b/src/Contao/View/Contao2BackendView/ActionHandler/SelectHandler.php index 51686281..3dc3fe4c 100644 --- a/src/Contao/View/Contao2BackendView/ActionHandler/SelectHandler.php +++ b/src/Contao/View/Contao2BackendView/ActionHandler/SelectHandler.php @@ -47,6 +47,7 @@ * * This class handles multiple actions. * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) */ class SelectHandler diff --git a/src/Contao/View/Contao2BackendView/ActionHandler/ShowHandler.php b/src/Contao/View/Contao2BackendView/ActionHandler/ShowHandler.php index 25d5b2e9..ab6e46ae 100644 --- a/src/Contao/View/Contao2BackendView/ActionHandler/ShowHandler.php +++ b/src/Contao/View/Contao2BackendView/ActionHandler/ShowHandler.php @@ -42,6 +42,8 @@ /** * Handler class for handling the "show" action. + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class ShowHandler { diff --git a/src/Contao/View/Contao2BackendView/ActionHandler/ToggleHandler.php b/src/Contao/View/Contao2BackendView/ActionHandler/ToggleHandler.php index 17fa4a53..d688a3fc 100644 --- a/src/Contao/View/Contao2BackendView/ActionHandler/ToggleHandler.php +++ b/src/Contao/View/Contao2BackendView/ActionHandler/ToggleHandler.php @@ -41,6 +41,8 @@ /** * This class handles toggle commands. + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class ToggleHandler { diff --git a/src/Contao/View/Contao2BackendView/BaseView.php b/src/Contao/View/Contao2BackendView/BaseView.php index eb10831b..54fbe2bd 100644 --- a/src/Contao/View/Contao2BackendView/BaseView.php +++ b/src/Contao/View/Contao2BackendView/BaseView.php @@ -53,6 +53,8 @@ * This class is the base class for the different backend view mode sub classes. * * @SuppressWarnings(PHPMD.TooManyPublicMethods) + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) */ class BaseView implements BackendViewInterface, EventSubscriberInterface { diff --git a/src/Contao/View/Contao2BackendView/ButtonRenderer.php b/src/Contao/View/Contao2BackendView/ButtonRenderer.php index 18d57148..8a4d530c 100644 --- a/src/Contao/View/Contao2BackendView/ButtonRenderer.php +++ b/src/Contao/View/Contao2BackendView/ButtonRenderer.php @@ -53,6 +53,9 @@ /** * This class is an helper for rendering the operation buttons in the views. + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) */ class ButtonRenderer { @@ -518,8 +521,8 @@ private function calculateClipboardItems() $filter = new Filter(); $filter->andModelIsFromProvider($basicDefinition->getDataProvider()); - if ($parentDataProviderName = $basicDefinition->getParentDataProvider()) { - $filter->andParentIsFromProvider($parentDataProviderName); + if ($parentProviderName = $basicDefinition->getParentDataProvider()) { + $filter->andParentIsFromProvider($parentProviderName); } else { $filter->andHasNoParent(); } diff --git a/src/Contao/View/Contao2BackendView/ContaoWidgetManager.php b/src/Contao/View/Contao2BackendView/ContaoWidgetManager.php index 0cf7ca5e..afb69823 100644 --- a/src/Contao/View/Contao2BackendView/ContaoWidgetManager.php +++ b/src/Contao/View/Contao2BackendView/ContaoWidgetManager.php @@ -51,6 +51,8 @@ * This class is responsible for creating widgets and processing data through them. * * @SuppressWarnings(PHPMD.LongClassName) + * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class ContaoWidgetManager { @@ -172,14 +174,14 @@ public function loadRichTextEditor($buffer, Widget $widget) return $buffer; } - $backendAdapter = $this->framework->getAdapter(Backend::class); - $templateLoaderAdapter = $this->framework->getAdapter(TemplateLoader::class); + $backendAdapter = $this->framework->getAdapter(Backend::class); + $templateLoader = $this->framework->getAdapter(TemplateLoader::class); [$file, $type] = \explode('|', $widget->rte) + [null, null]; $templateName = 'be_' . $file; // This test if the rich text editor template exist. - $templateLoaderAdapter->getPath($templateName, 'html5'); + $templateLoader->getPath($templateName, 'html5'); $template = new ContaoBackendViewTemplate($templateName); $template diff --git a/src/Contao/View/Contao2BackendView/Controller/ClipboardController.php b/src/Contao/View/Contao2BackendView/Controller/ClipboardController.php index a2ee91df..d1d20cd6 100644 --- a/src/Contao/View/Contao2BackendView/Controller/ClipboardController.php +++ b/src/Contao/View/Contao2BackendView/Controller/ClipboardController.php @@ -44,6 +44,8 @@ /** * Class ClipboardController. + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class ClipboardController implements EventSubscriberInterface { @@ -337,9 +339,9 @@ public function handleView(ViewEvent $event) continue; } - $formatModelLabelEvent = new FormatModelLabelEvent($environment, $model); - $eventDispatcher->dispatch($formatModelLabelEvent, DcGeneralEvents::FORMAT_MODEL_LABEL); - $label = $formatModelLabelEvent->getLabel(); + $formatModelLabel = new FormatModelLabelEvent($environment, $model); + $eventDispatcher->dispatch($formatModelLabel, DcGeneralEvents::FORMAT_MODEL_LABEL); + $label = $formatModelLabel->getLabel(); $label = \array_shift($label); $label = $label['content']; } else { diff --git a/src/Contao/View/Contao2BackendView/EditMask.php b/src/Contao/View/Contao2BackendView/EditMask.php index e3024561..8f1ef520 100644 --- a/src/Contao/View/Contao2BackendView/EditMask.php +++ b/src/Contao/View/Contao2BackendView/EditMask.php @@ -59,6 +59,7 @@ * It also handles the persisting of the model. * * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class EditMask { diff --git a/src/Contao/View/Contao2BackendView/EventListener/CreateModelButtonListener.php b/src/Contao/View/Contao2BackendView/EventListener/CreateModelButtonListener.php index b77c6c4f..b0fb45e0 100644 --- a/src/Contao/View/Contao2BackendView/EventListener/CreateModelButtonListener.php +++ b/src/Contao/View/Contao2BackendView/EventListener/CreateModelButtonListener.php @@ -65,8 +65,8 @@ public function handle(GetGlobalButtonEvent $event) ) { $filter = new Filter(); $filter->andModelIsFromProvider($basicDefinition->getDataProvider()); - if ($parentDataProviderName = $basicDefinition->getParentDataProvider()) { - $filter->andParentIsFromProvider($parentDataProviderName); + if ($parentProviderName = $basicDefinition->getParentDataProvider()) { + $filter->andParentIsFromProvider($parentProviderName); } else { $filter->andHasNoParent(); } diff --git a/src/Contao/View/Contao2BackendView/FileSelect.php b/src/Contao/View/Contao2BackendView/FileSelect.php index 736f1b09..a1bd788e 100644 --- a/src/Contao/View/Contao2BackendView/FileSelect.php +++ b/src/Contao/View/Contao2BackendView/FileSelect.php @@ -49,6 +49,8 @@ * Back end tree picker for usage in generalfile.php. * * @deprecated This is deprecated since 2.1 and where removed in 3.0. Use the file tree widget instead. + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class FileSelect { diff --git a/src/Contao/View/Contao2BackendView/PanelBuilder.php b/src/Contao/View/Contao2BackendView/PanelBuilder.php index 22bc3ee2..b391f4e1 100644 --- a/src/Contao/View/Contao2BackendView/PanelBuilder.php +++ b/src/Contao/View/Contao2BackendView/PanelBuilder.php @@ -39,6 +39,8 @@ /** * This class builds a panel for an environment. + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class PanelBuilder { diff --git a/src/Contao/View/Contao2BackendView/Subscriber/WidgetBuilder.php b/src/Contao/View/Contao2BackendView/Subscriber/WidgetBuilder.php index 930c7863..1851a5c9 100644 --- a/src/Contao/View/Contao2BackendView/Subscriber/WidgetBuilder.php +++ b/src/Contao/View/Contao2BackendView/Subscriber/WidgetBuilder.php @@ -50,6 +50,9 @@ /** * Widget Builder build Contao backend widgets. + * + * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class WidgetBuilder implements EnvironmentAwareInterface { diff --git a/src/Contao/View/Contao2BackendView/TreePicker.php b/src/Contao/View/Contao2BackendView/TreePicker.php index 83dbc6a5..07288933 100644 --- a/src/Contao/View/Contao2BackendView/TreePicker.php +++ b/src/Contao/View/Contao2BackendView/TreePicker.php @@ -62,8 +62,11 @@ * Provide methods to handle input field "tableTree". * * @SuppressWarnings(PHPMD.TooManyPublicMethods) + * @SuppressWarnings(PHPMD.TooManyFields) + * @SuppressWarnings(PHPMD.TooManyMethods) * @SuppressWarnings(PHPMD.ExcessiveClassLength) * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class TreePicker extends Widget { diff --git a/src/Contao/View/Contao2BackendView/TreeSelect.php b/src/Contao/View/Contao2BackendView/TreeSelect.php index 0e5d1057..f9454364 100644 --- a/src/Contao/View/Contao2BackendView/TreeSelect.php +++ b/src/Contao/View/Contao2BackendView/TreeSelect.php @@ -42,6 +42,8 @@ * Class TreeSelect. * * Back end tree picker for usage in generaltree.php. + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class TreeSelect { diff --git a/src/Contao/View/Contao2BackendView/TreeView.php b/src/Contao/View/Contao2BackendView/TreeView.php index 8e78d234..d7fa49f6 100644 --- a/src/Contao/View/Contao2BackendView/TreeView.php +++ b/src/Contao/View/Contao2BackendView/TreeView.php @@ -56,6 +56,9 @@ * Class TreeView. * * Implementation for tree displaying. + * + * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class TreeView extends BaseView { @@ -444,8 +447,8 @@ protected function viewTree($collection) $filter = new Filter(); $filter->andModelIsFromProvider($basicDefinition->getDataProvider()); - if ($parentDataProviderName = $basicDefinition->getParentDataProvider()) { - $filter->andParentIsFromProvider($parentDataProviderName); + if ($parentProviderName = $basicDefinition->getParentDataProvider()) { + $filter->andParentIsFromProvider($parentProviderName); } else { $filter->andHasNoParent(); } diff --git a/src/Contao/View/Contao2BackendView/ViewHelpers.php b/src/Contao/View/Contao2BackendView/ViewHelpers.php index 8b8b0d72..8dd924dd 100644 --- a/src/Contao/View/Contao2BackendView/ViewHelpers.php +++ b/src/Contao/View/Contao2BackendView/ViewHelpers.php @@ -94,12 +94,12 @@ public static function getManualSortingProperty(EnvironmentInterface $environmen if (null === $definition) { /** @var Contao2BackendViewDefinitionInterface $viewDefinition */ - $dataDefinition = $environment->getDataDefinition(); - $viewDefinition = $dataDefinition->getDefinition(Contao2BackendViewDefinitionInterface::NAME); - $groupAndSortingDefinition = $viewDefinition->getListingConfig()->getGroupAndSortingDefinition(); + $dataDefinition = $environment->getDataDefinition(); + $viewDefinition = $dataDefinition->getDefinition(Contao2BackendViewDefinitionInterface::NAME); + $groupAndSorting = $viewDefinition->getListingConfig()->getGroupAndSortingDefinition(); - if ($groupAndSortingDefinition->hasDefault()) { - $definition = $groupAndSortingDefinition->getDefault(); + if ($groupAndSorting->hasDefault()) { + $definition = $groupAndSorting->getDefault(); } } diff --git a/src/Contao/View/Contao2BackendView/Widget/FileTree.php b/src/Contao/View/Contao2BackendView/Widget/FileTree.php index d89da38b..5a450f46 100644 --- a/src/Contao/View/Contao2BackendView/Widget/FileTree.php +++ b/src/Contao/View/Contao2BackendView/Widget/FileTree.php @@ -48,6 +48,9 @@ * @property bool isDownloads If true only allowed download files are listed. * * @see https://github.com/contao/core/blob/master/system/modules/core/widgets/FileTree.php + * + * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class FileTree extends AbstractWidget { diff --git a/src/Controller/BackendTreeController.php b/src/Controller/BackendTreeController.php index 0a49a1bf..d6f25fe5 100644 --- a/src/Controller/BackendTreeController.php +++ b/src/Controller/BackendTreeController.php @@ -47,6 +47,8 @@ * Handles the backend tree. * * @Route("/contao/cca", defaults={"_scope" = "backend", "_token_check" = true}) + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class BackendTreeController implements ContainerAwareInterface { diff --git a/src/Controller/DefaultController.php b/src/Controller/DefaultController.php index 89588c70..03e3ba57 100644 --- a/src/Controller/DefaultController.php +++ b/src/Controller/DefaultController.php @@ -70,6 +70,9 @@ * @SuppressWarnings(PHPMD.ExcessiveClassLength) * @SuppressWarnings(PHPMD.TooManyPublicMethods) * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) + * @SuppressWarnings(PHPMD.TooManyFields) + * @SuppressWarnings(PHPMD.TooManyMethods) + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class DefaultController implements ControllerInterface { @@ -949,6 +952,8 @@ private function triggerPostPasteModel(CollectionInterface $collection) * @param array $deepCopyList The deep copy list. * * @return void + * + * @SuppressWarnings(PHPMD.LongVariable) */ protected function doDeepCopy(array $deepCopyList) { diff --git a/src/Controller/ModelCollector.php b/src/Controller/ModelCollector.php index 08f88829..fe60a8e1 100644 --- a/src/Controller/ModelCollector.php +++ b/src/Controller/ModelCollector.php @@ -39,6 +39,9 @@ /** * This class provides methods for retrieval of models. + * + * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class ModelCollector { diff --git a/src/DC/General.php b/src/DC/General.php index b67cf63c..f007e61e 100644 --- a/src/DC/General.php +++ b/src/DC/General.php @@ -47,6 +47,7 @@ * This class is only present so Contao can instantiate a backend properly as it needs a \DataContainer descendant. * * @SuppressWarnings(PHPMD.TooManyPublicMethods) + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class General extends DataContainer implements DataContainerInterface { diff --git a/src/Data/DefaultCollection.php b/src/Data/DefaultCollection.php index 992200a5..35e75c0e 100644 --- a/src/Data/DefaultCollection.php +++ b/src/Data/DefaultCollection.php @@ -34,6 +34,8 @@ * Internally it simply holds an array. * * @SuppressWarnings(PHPMD.TooManyPublicMethods) We have to keep them as we implement the interfaces. + * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) - There is no elegant way to reduce this class more without + * reducing the interface. */ class DefaultCollection implements CollectionInterface { diff --git a/src/Data/DefaultDataProvider.php b/src/Data/DefaultDataProvider.php index 1025320b..8dbf247a 100644 --- a/src/Data/DefaultDataProvider.php +++ b/src/Data/DefaultDataProvider.php @@ -1,1030 +1,1031 @@ - - * @author Stefan Heimes - * @author Tristan Lins - * @author Andreas Isaak - * @author David Molineus - * @author Oliver Hoff - * @author Patrick Kahl - * @author Simon Kusterer - * @author Christopher Boelter - * @author Sven Baumann - * @author Ingolf Steinhardt - * @author Richard Henkenjohann - * @author Alex Wuttke - * @copyright 2013-2022 Contao Community Alliance. - * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later - * @filesource - */ - -namespace ContaoCommunityAlliance\DcGeneral\Data; - -use Contao\BackendUser; -use Contao\Database; -use Contao\StringUtil; -use Contao\System; -use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralRuntimeException; -use Doctrine\DBAL\Connection; -use Doctrine\DBAL\Schema\Table; - -/** - * Class DefaultDataProvider. - * - * Default implementation for a data provider using the Contao default database as backend. - * - * @SuppressWarnings(PHPMD.TooManyPublicMethods) - We have to keep them as we implement the interfaces. - * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) - There is no elegant way to reduce this class more without - * reducing the interface. - */ -class DefaultDataProvider implements DataProviderInterface -{ - /** - * Name of current source. - * - * @var string - */ - protected $source; - - /** - * The Database instance. - * - * @var Connection - */ - protected $connection; - - /** - * The name of the id property. - * - * @var string - */ - protected $idProperty = 'id'; - - /** - * The property that shall get populated with the current timestamp when saving data. - * - * @var string - */ - protected $timeStampProperty = false; - - /** - * The id generator to use (if any). - * - * @var IdGeneratorInterface - */ - protected $idGenerator; - - /** - * The table schema. - * - * @var Table - */ - private $schema; - - /** - * Retrieve the name of the id property. - * - * @return string - */ - public function getIdProperty() - { - return $this->idProperty; - } - - /** - * Set the id property. - * - * @param string $idProperty The name of the id property. - * - * @return DefaultDataProvider - */ - public function setIdProperty($idProperty) - { - $this->idProperty = $idProperty; - - return $this; - } - - /** - * Get the property name that shall get updated with the current time stamp when saving to the database. - * - * @return string|null - */ - public function getTimeStampProperty() - { - return $this->timeStampProperty; - } - - /** - * Set the property name that shall get updated with the current time stamp when saving to the database. - * - * @param boolean $timeStampField The property name or empty to clear. - * - * @return DefaultDataProvider - */ - public function setTimeStampProperty($timeStampField = null) - { - $this->timeStampProperty = $timeStampField; - - return $this; - } - - /** - * Set the id generator to use. - * - * @param IdGeneratorInterface $idGenerator The id generator. - * - * @return DefaultDataProvider - */ - public function setIdGenerator($idGenerator) - { - $this->idGenerator = $idGenerator; - - return $this; - } - - /** - * Retrieve the id generator. - * - * @return IdGeneratorInterface - */ - public function getIdGenerator() - { - return $this->idGenerator; - } - - /** - * Create an instance of the default database driven uuid generator. - * - * @return DefaultDataProvider - * - * @throws DcGeneralRuntimeException When already an id generator has been set on the instance. - */ - public function enableDefaultUuidGenerator() - { - if ($this->idGenerator) { - throw new DcGeneralRuntimeException('Error: already an id generator set on database provider.'); - } - - $this->setIdGenerator(new DatabaseUuidIdGenerator($this->connection)); - - return $this; - } - - /** - * {@inheritDoc} - * - * @throws DcGeneralRuntimeException When no source has been defined. - * @throws DcGeneralRuntimeException For invalid database connection. - * - * @SuppressWarnings(PHPMD.CyclomaticComplexity) - * @SuppressWarnings(PHPMD.NPathComplexity) - */ - public function setBaseConfig(array $config) - { - // Check configuration. - if (!isset($config['source'])) { - throw new DcGeneralRuntimeException('Missing table name.'); - } - - $this->fallbackFromDatabaseToConnection($config); - - if (isset($config['connection'])) { - if (!($config['connection'] instanceof Connection)) { - throw new DcGeneralRuntimeException('Invalid database connection.'); - } - - $this->connection = $config['connection']; - } else { - // @codingStandardsIgnoreStart - @\trigger_error( - 'You should pass a doctrine database connection to "' . __METHOD__ . '".', - E_USER_DEPRECATED - ); - // @codingStandardsIgnoreEnd - - $this->connection = $this->getDefaultConnection(); - } - - if (isset($config['idGenerator'])) { - if ($config['idGenerator'] instanceof IdGeneratorInterface) { - $idGenerator = $config['idGenerator']; - $this->setIdGenerator($idGenerator); - } - } - - $this->source = $config['source']; - - if (isset($config['timeStampProperty'])) { - $this->setTimeStampProperty($config['timeStampProperty']); - } elseif ($this->fieldExists('tstamp')) { - $this->setTimeStampProperty('tstamp'); - } - - if (isset($config['idProperty'])) { - $this->setIdProperty($config['idProperty']); - } - } - - /** - * {@inheritDoc} - */ - public function getEmptyConfig() - { - return DefaultConfig::init(); - } - - /** - * {@inheritDoc} - */ - public function getEmptyModel() - { - $model = new DefaultModel(); - $model->setProviderName($this->source); - return $model; - } - - /** - * {@inheritDoc} - */ - public function getEmptyCollection() - { - return new DefaultCollection(); - } - - /** - * Fetch an empty single filter option collection (new model list). - * - * @return FilterOptionCollectionInterface - * - * @deprecated This method was never intended to be used externally. - */ - public function getEmptyFilterOptionCollection() - { - // @codingStandardsIgnoreStart - @\trigger_error( - 'Method ' . __METHOD__ . ' was never intended to be called via interface and will get removed', - E_USER_DEPRECATED - ); - // @codingStandardsIgnoreEnd - return new DefaultFilterOptionCollection(); - } - - /** - * {@inheritDoc} - * - * @throws DcGeneralRuntimeException When an unusable object has been passed. - */ - public function delete($item) - { - $modelId = null; - if (\is_numeric($item) || \is_string($item)) { - $modelId = $item; - } elseif (\is_object($item) && $item instanceof ModelInterface && null !== $item->getId()) { - $modelId = $item->getId(); - } else { - throw new DcGeneralRuntimeException("ID missing or given object not of type 'ModelInterface'."); - } - - // Insert undo. - $this->insertUndo( - \sprintf( - 'DELETE FROM %1$s WHERE %1$s.id = %2$s', - $this->source, - $modelId - ), - \sprintf( - 'SELECT * FROM %1$s WHERE %1$s.id = %2$s', - $this->source, - $modelId - ), - $this->source - ); - - $this->connection->delete($this->source, [$this->source . '.id' => $modelId]); - } - - /** - * Create a model from a database result. - * - * @param array $result The database result to create a model from. - * - * @return ModelInterface - */ - protected function createModelFromDatabaseResult(array $result) - { - $model = $this->getEmptyModel(); - - foreach ($result as $key => $value) { - if ($key === $this->idProperty) { - $model->setIdRaw($value); - } - - $model->setPropertyRaw($key, StringUtil::deserialize($value)); - } - - return $model; - } - - /** - * {@inheritDoc} - * - * @throws \Doctrine\DBAL\Exception - */ - public function fetch(ConfigInterface $config) - { - $queryBuilder = $this->connection->createQueryBuilder(); - $queryBuilder->from($this->source); - DefaultDataProviderDBalUtils::addField($config, $this->idProperty, $queryBuilder); - - if (null !== $config->getId()) { - $queryBuilder->where($this->source . '.id=:id'); - $queryBuilder->setParameter('id', $config->getId()); - } - - if (null === $config->getId()) { - DefaultDataProviderDBalUtils::addWhere($config, $queryBuilder); - DefaultDataProviderDBalUtils::addSorting($config, $queryBuilder); - - $queryBuilder->setMaxResults(1); - $queryBuilder->setFirstResult(0); - } - - $statement = $queryBuilder->executeQuery(); - if (0 === $statement->rowCount()) { - return null; - } - - $result = $statement->fetchAssociative(); - - return $this->createModelFromDatabaseResult($result); - } - - /** - * {@inheritDoc} - * - * @throws \Doctrine\DBAL\Exception - */ - public function fetchAll(ConfigInterface $config) - { - $queryBuilder = $this->connection->createQueryBuilder(); - $queryBuilder->from($this->source); - DefaultDataProviderDBalUtils::addField($config, $this->idProperty, $queryBuilder); - DefaultDataProviderDBalUtils::addWhere($config, $queryBuilder); - DefaultDataProviderDBalUtils::addSorting($config, $queryBuilder); - - if (0 !== $config->getAmount()) { - $queryBuilder->setMaxResults($config->getAmount()); - $queryBuilder->setFirstResult($config->getStart() ?? 0); - } - - $collection = $this->getEmptyCollection(); - - $statement = $queryBuilder->executeQuery(); - if (0 === $statement->rowCount()) { - return $collection; - } - - if ($config->getIdOnly()) { - return $statement->fetchFirstColumn(); - } - - $result = $statement->fetchAllAssociative(); - - foreach ($result as $item) { - $collection->push($this->createModelFromDatabaseResult($item)); - } - - return $collection; - } - - /** - * {@inheritDoc} - * - * @throws DcGeneralRuntimeException If improper values have been passed (i.e. not exactly one field requested). - * @throws \Doctrine\DBAL\Exception - */ - public function getFilterOptions(ConfigInterface $config) - { - $internalConfig = $this->prefixDataProviderProperties($config); - $properties = $internalConfig->getFields(); - if (1 !== \count($properties)) { - throw new DcGeneralRuntimeException('objConfig must contain exactly one property to be retrieved.'); - } - $property = $properties[0]; - - $queryBuilder = $this->connection->createQueryBuilder(); - $queryBuilder->select('DISTINCT(' . $property . ')'); - $queryBuilder->from($this->source); - DefaultDataProviderDBalUtils::addWhere($internalConfig, $queryBuilder); - - $statement = $queryBuilder->executeQuery(); - - $values = $statement->fetchAllAssociative(); - - $filterProperties = $config->getFields(); - if (1 !== \count($filterProperties)) { - throw new DcGeneralRuntimeException('objConfig must contain exactly one property to be retrieved.'); - } - $filterProperty = $filterProperties[0]; - - $collection = new DefaultFilterOptionCollection(); - foreach ($values as $value) { - $collection->add($value[$filterProperty], $value[$filterProperty]); - } - - return $collection; - } - - /** - * {@inheritDoc} - * - * @throws \Doctrine\DBAL\Exception - */ - public function getCount(ConfigInterface $config) - { - $internalConfig = $this->prefixDataProviderProperties($config); - $queryBuilder = $this->connection->createQueryBuilder(); - $queryBuilder->select('COUNT(*) AS count'); - $queryBuilder->from($this->source); - DefaultDataProviderDBalUtils::addWhere($internalConfig, $queryBuilder); - - $statement = $queryBuilder->executeQuery(); - - return $statement->fetchFirstColumn(); - } - - /** - * {@inheritDoc} - * @throws \Doctrine\DBAL\Exception - */ - public function isUniqueValue($field, $new, $primaryId = null) - { - $queryBuilder = $this->connection->createQueryBuilder(); - $queryBuilder->select('*'); - $queryBuilder->from($this->source); - $queryBuilder->where($queryBuilder->expr()->eq($this->source . '.' . $field, ':' . $field)); - $queryBuilder->setParameter($field, $new); - - $statement = $queryBuilder->executeQuery(); - $unique = $statement->fetchAssociative(); - - if (0 === $statement->rowCount()) { - return true; - } - - if (($primaryId === $unique['id']) && (1 === $statement->rowCount())) { - return true; - } - - return false; - } - - /** - * {@inheritDoc} - */ - public function resetFallback($field) - { - // @codingStandardsIgnoreStart - @\trigger_error( - __CLASS__ . '::' . __METHOD__ . ' is deprecated - handle resetting manually', - E_USER_DEPRECATED - ); - // @codingStandardsIgnoreEnd - - $this->connection->query('UPDATE ' . $this->source . ' SET ' . $field . ' = \'\'')->execute(); - } - - /** - * Prefix the data provider properties. - * - * @param ConfigInterface $config The config. - * - * @return ConfigInterface - */ - private function prefixDataProviderProperties(ConfigInterface $config) - { - $internalConfig = clone $config; - $this->sortingPrefixer($internalConfig); - - if (null !== ($filter = $internalConfig->getFilter())) { - $this->filterPrefixer($filter); - $internalConfig->setFilter($filter); - } - - if (null !== ($fields = $internalConfig->getFields())) { - $this->fieldPrefixer($fields); - $internalConfig->setFields($fields); - } - - return $internalConfig; - } - - /** - * The config sorting prefixer. - * - * @param ConfigInterface $config The config. - * - * @return void - */ - private function sortingPrefixer(ConfigInterface $config) - { - $sorting = []; - foreach ($config->getSorting() as $property => $value) { - if (0 === \strpos($property, $this->source . '.')) { - $sorting[$property] = $value; - - continue; - } - - if (!$this->fieldExists($property)) { - continue; - } - - $sorting[$this->source . '.' . $property] = $value; - } - $config->setSorting($sorting); - } - - /** - * The filter prefixer. - * - * @param array $filter The filter setting. - * - * @return void - */ - private function filterPrefixer(array &$filter) - { - foreach ($filter as &$child) { - if (\array_key_exists('property', $child) - && (false === \strpos($child['property'], $this->source . '.')) - && $this->fieldExists($child['property']) - ) { - $child['property'] = $this->source . '.' . $child['property']; - } - - if (\array_key_exists('children', $child)) { - $this->filterPrefixer($child['children']); - } - } - } - - /** - * The field prefixer. - * - * @param array $fields The fields. - * - * @return void - */ - private function fieldPrefixer(array &$fields) - { - foreach ($fields as $index => $property) { - if (0 === \strpos($property, $this->source . '.') - || !$this->fieldExists($property) - ) { - continue; - } - - $fields[$index] = $this->source . '.' . $property; - } - } - - /** - * Convert a model into a property array to be used in insert and update queries. - * - * @param ModelInterface $model The model to convert into an property array. - * @param int $timestamp Optional the timestamp. - * - * @return array - * - * @SuppressWarnings(PHPMD.Superglobals) - */ - private function convertModelToDataPropertyArray(ModelInterface $model, int $timestamp) - { - $data = []; - foreach ($model as $key => $value) { - if (($key === $this->idProperty) || !$this->fieldExists($key)) { - continue; - } - - if (\is_array($value)) { - $data[$this->source . '.' . $key] = \serialize($value); - } else { - $data[$this->source . '.' . $key] = $value; - } - } - - if ($this->timeStampProperty) { - $data[$this->source . '.' . $this->getTimeStampProperty()] = $timestamp ?: \time(); - } - - return $data; - } - - /** - * Insert the model into the database. - * - * @param ModelInterface $model The model to insert into the database. - * @param int $timestamp Optional the timestamp. - * - * @return void - */ - private function insertModelIntoDatabase(ModelInterface $model, $timestamp = 0) - { - $data = $this->convertModelToDataPropertyArray($model, $timestamp); - if ($this->getIdGenerator()) { - $model->setId($this->getIdGenerator()->generate()); - $data[$this->idProperty] = $model->getId(); - } - - $this->connection->insert($this->source, $data); - - $insertId = $this->connection->lastInsertId($this->source); - - if (('' !== $insertId) && !isset($data[$this->idProperty])) { - $model->setId($insertId); - } - } - - /** - * Update the model in the database. - * - * @param ModelInterface $model The model to update the database. - * @param int $timestamp Optional the timestamp. - * - * @return void - */ - private function updateModelInDatabase($model, $timestamp = 0) - { - $data = $this->convertModelToDataPropertyArray($model, $timestamp); - - $this->connection->update($this->source, $data, ['id' => $model->getId()]); - } - - /** - * {@inheritDoc} - */ - public function save(ModelInterface $item, $timestamp = 0) - { - if (\in_array($item->getId(), [null, ''])) { - $this->insertModelIntoDatabase($item); - } else { - $this->updateModelInDatabase($item); - } - - return $item; - } - - /** - * {@inheritDoc} - */ - public function saveEach(CollectionInterface $items, $timestamp = 0) - { - foreach ($items as $value) { - $this->save($value, $timestamp); - } - } - - /** - * {@inheritDoc} - */ - public function fieldExists($columnName) - { - if (!isset($this->schema)) { - $this->schema = $this->connection->getSchemaManager()->listTableDetails($this->source); - } - - return $this->schema->hasColumn($columnName); - } - - /** - * {@inheritDoc} - * - * @throws \Doctrine\DBAL\Exception - */ - public function getVersion($mixID, $mixVersion) - { - $queryBuilder = $this->connection->createQueryBuilder(); - $queryBuilder->select('*'); - $queryBuilder->from('tl_version'); - $queryBuilder->andWhere($queryBuilder->expr()->eq('tl_version.pid', ':pid')); - $queryBuilder->setParameter('pid', $mixID); - $queryBuilder->andWhere($queryBuilder->expr()->eq('tl_version.version', ':version')); - $queryBuilder->setParameter('version', $mixVersion); - $queryBuilder->andWhere($queryBuilder->expr()->eq('tl_version.fromTable', ':fromTable')); - $queryBuilder->setParameter('fromTable', $this->source); - - $statement = $queryBuilder->executeQuery(); - if (0 === $statement->rowCount()) { - return null; - } - - if (null === $version = $statement->fetchAllAssociative()) { - return null; - } - - $data = StringUtil::deserialize($version['data']); - - if (!\is_array($data) || (0 === \count($data))) { - return null; - } - - $model = $this->getEmptyModel(); - $model->setID($mixID); - foreach ($data as $key => $value) { - if ($key === $this->idProperty) { - continue; - } - - $model->setProperty($key, $value); - } - - return $model; - } - - /** - * Return a list with all versions for the row with the given Id. - * - * @param mixed $mixID The ID of the row. - * @param boolean $onlyActive If true, only active versions will get returned, if false all version will get - * returned. - * - * @return CollectionInterface - * - * @throws \Doctrine\DBAL\Exception - */ - public function getVersions($mixID, $onlyActive = false) - { - $queryBuilder = $this->connection->createQueryBuilder(); - $queryBuilder->select(['tstamp', 'version', 'username', 'active']); - $queryBuilder->from('tl_version'); - $queryBuilder->andWhere($queryBuilder->expr()->eq('tl_version.fromTable', ':fromTable')); - $queryBuilder->setParameter('fromTable', $this->source); - $queryBuilder->andWhere($queryBuilder->expr()->eq('tl_version.pid', ':pid')); - $queryBuilder->setParameter('pid', $mixID); - - if ($onlyActive) { - $queryBuilder->andWhere($queryBuilder->expr()->eq('tl_version.active', ':active')); - $queryBuilder->setParameter('active', '1'); - } else { - $queryBuilder->orderBy('tl_version.version', 'DESC'); - } - - $statement = $queryBuilder->executeQuery(); - if (0 === $statement->rowCount()) { - return null; - } - - $versions = $statement->fetchAllAssociative(); - - $collection = $this->getEmptyCollection(); - - foreach ((array) $versions as $versionValue) { - $model = $this->getEmptyModel(); - $model->setId($mixID); - - foreach ($versionValue as $key => $value) { - if ($key === $this->idProperty) { - continue; - } - - $model->setProperty($key, $value); - } - - $collection->push($model); - } - - return $collection; - } - - /** - * Save a new version of a row. - * - * @param ModelInterface $model The model for which a new version shall be created. - * @param string $username The username to attach to the version as creator. - * - * @return void - * - * @throws \Doctrine\DBAL\Exception - */ - public function saveVersion(ModelInterface $model, $username) - { - $queryBuilder = $this->connection->createQueryBuilder(); - $queryBuilder->select('COUNT(*) AS count'); - $queryBuilder->from('tl_version'); - $queryBuilder->andWhere($queryBuilder->expr()->eq('tl_version.pid', ':pid')); - $queryBuilder->setParameter('pid', $model->getId()); - $queryBuilder->andWhere($queryBuilder->expr()->eq('tl_version.fromTable', ':fromTable')); - $queryBuilder->setParameter('fromTable', $this->source); - - $statement = $queryBuilder->executeQuery(); - $count = $statement->fetchFirstColumn(); - - $mixNewVersion = ((int) $count + 1); - $mixData = $model->getPropertiesAsArray(); - - $mixData[$this->idProperty] = $model->getId(); - - $insert = [ - 'tl_version.pid' => $model->getId(), - 'tl_version.tstamp' => \time(), - 'tl_version.version' => $mixNewVersion, - 'tl_version.fromTable' => $this->source, - 'tl_version.username' => $username, - 'tl_version.data' => \serialize($mixData) - ]; - - $this->connection->insert('tl_version', $insert); - - $this->setVersionActive($model->getId(), $mixNewVersion); - } - - /** - * Set a version as active. - * - * @param mixed $mixID The ID of the row. - * @param mixed $mixVersion The version number to set active. - * - * @return void - */ - public function setVersionActive($mixID, $mixVersion) - { - $updateValues = ['tl_version.pid' => $mixID, 'tl_version.fromTable' => $this->source]; - - // Set version inactive. - $this->connection->update('tl_version', ['tl_version.active' => ''], $updateValues); - - // Set version active. - $updateValues['version'] = $mixVersion; - $this->connection->update('tl_version', ['tl_version.active' => 1], $updateValues); - } - - /** - * Retrieve the current active version for a row. - * - * @param mixed $mixID The ID of the row. - * - * @return mixed The current version number of the requested row. - * - * @throws \Doctrine\DBAL\Exception - */ - public function getActiveVersion($mixID) - { - $queryBuilder = $this->connection->createQueryBuilder(); - $queryBuilder->select('version'); - $queryBuilder->from('tl_version'); - $queryBuilder->andWhere($queryBuilder->expr()->eq('tl_version.pid', ':pid')); - $queryBuilder->setParameter('pid', $mixID); - $queryBuilder->andWhere($queryBuilder->expr()->eq('tl_version.fromTable', ':fromTable')); - $queryBuilder->setParameter('fromTable', $this->source); - $queryBuilder->andWhere($queryBuilder->expr()->eq('tl_version.active', ':active')); - $queryBuilder->setParameter('active', 1); - - $statement = $queryBuilder->executeQuery(); - if (0 === $statement->rowCount()) { - return null; - } - - return $statement->fetchAllAssociative()['version']; - } - - /** - * Check if two models have the same values in all properties. - * - * @param ModelInterface $firstModel The first model to compare. - * @param ModelInterface $secondModel The second model to compare. - * - * @return boolean True - If both models are same, false if not. - */ - public function sameModels($firstModel, $secondModel) - { - foreach ($firstModel as $key => $value) { - if ($key === $this->idProperty) { - continue; - } - - if (\is_array($value)) { - if (!\is_array($secondModel->getProperty($key))) { - return false; - } - - if (\serialize($value) !== \serialize($secondModel->getProperty($key))) { - return false; - } - } elseif ($value !== $secondModel->getProperty($key)) { - return false; - } - } - - return true; - } - - /** - * Store an undo entry in the table tl_undo. - * - * Currently, this only supports delete queries. - * - * @param string $sourceSQL The SQL used to perform the action to be undone. - * @param string $saveSQL The SQL query to retrieve the current entries. - * @param string $table The table to be affected by the action. - * - * @return void - * @throws \Doctrine\DBAL\Exception - */ - protected function insertUndo($sourceSQL, $saveSQL, $table) - { - // Load row. - $statement = $this->connection->executeQuery($saveSQL); - - // Check if we have a result. - if (0 === $statement->rowCount()) { - return; - } - - $result = $statement->fetchAssociative(); - - // Save information in array. - $parameters = []; - foreach ($result as $value) { - $parameters[$table][] = $value; - } - - $prefix = '(DC General)'; - $user = BackendUser::getInstance(); - - // Write into undo. - $this->connection->insert( - 'tl_undo', - [ - 'tl_undo.pid' => $user->id, - 'tl_undo.tstamp' => \time(), - 'tl_undo.fromTable' => $table, - 'tl_undo.query' => $prefix . $sourceSQL, - 'tl_undo.affectedRows' => \count($parameters[$table]), - 'tl_undo.data' => \serialize($parameters) - ] - ); - } - - /** - * Get the default connection for the database. - * - * @return Connection - */ - protected function getDefaultConnection() - { - return System::getContainer()->get('database_connection'); - } - - /** - * This is a fallback for get the connection instead of the old database. - * - * @param array $config The configuration. - * - * @return void - * - * @deprecated This method is deprecated since 2.1 and where removed in 3.0. The old database never used in future. - */ - private function fallbackFromDatabaseToConnection(array &$config) - { - if (isset($config['database'])) { - // @codingStandardsIgnoreStart - @\trigger_error( - 'Config key database is deprecated use instead connection. Fallback will be dropped.', - E_USER_DEPRECATED - ); - // @codingStandardsIgnoreEnd - - if (!isset($config['connection'])) { - $config['connection'] = $config['database']; - } - - unset($config['database']); - } - - if (isset($config['connection']) && $config['connection'] instanceof Database) { - // @codingStandardsIgnoreStart - @\trigger_error( - '"' . __METHOD__ . '" now accepts doctrine instances - ' . - 'passing Contao database instances is deprecated.', - E_USER_DEPRECATED - ); - // @codingStandardsIgnoreEnd - $reflection = new \ReflectionProperty(Database::class, 'resConnection'); - $reflection->setAccessible(true); - - $config['connection'] = $reflection->getValue($config['connection']); - } - } -} + + * @author Stefan Heimes + * @author Tristan Lins + * @author Andreas Isaak + * @author David Molineus + * @author Oliver Hoff + * @author Patrick Kahl + * @author Simon Kusterer + * @author Christopher Boelter + * @author Sven Baumann + * @author Ingolf Steinhardt + * @author Richard Henkenjohann + * @author Alex Wuttke + * @copyright 2013-2022 Contao Community Alliance. + * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later + * @filesource + */ + +namespace ContaoCommunityAlliance\DcGeneral\Data; + +use Contao\BackendUser; +use Contao\Database; +use Contao\StringUtil; +use Contao\System; +use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralRuntimeException; +use Doctrine\DBAL\Connection; +use Doctrine\DBAL\Schema\Table; + +/** + * Class DefaultDataProvider. + * + * Default implementation for a data provider using the Contao default database as backend. + * + * @SuppressWarnings(PHPMD.TooManyPublicMethods) - We have to keep them as we implement the interfaces. + * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) - There is no elegant way to reduce this class more without + * reducing the interface. + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) - Can not reduce without changing the interface. + */ +class DefaultDataProvider implements DataProviderInterface +{ + /** + * Name of current source. + * + * @var string + */ + protected $source; + + /** + * The Database instance. + * + * @var Connection + */ + protected $connection; + + /** + * The name of the id property. + * + * @var string + */ + protected $idProperty = 'id'; + + /** + * The property that shall get populated with the current timestamp when saving data. + * + * @var string + */ + protected $timeStampProperty = false; + + /** + * The id generator to use (if any). + * + * @var IdGeneratorInterface + */ + protected $idGenerator; + + /** + * The table schema. + * + * @var Table + */ + private $schema; + + /** + * Retrieve the name of the id property. + * + * @return string + */ + public function getIdProperty() + { + return $this->idProperty; + } + + /** + * Set the id property. + * + * @param string $idProperty The name of the id property. + * + * @return DefaultDataProvider + */ + public function setIdProperty($idProperty) + { + $this->idProperty = $idProperty; + + return $this; + } + + /** + * Get the property name that shall get updated with the current time stamp when saving to the database. + * + * @return string|null + */ + public function getTimeStampProperty() + { + return $this->timeStampProperty; + } + + /** + * Set the property name that shall get updated with the current time stamp when saving to the database. + * + * @param boolean $timeStampField The property name or empty to clear. + * + * @return DefaultDataProvider + */ + public function setTimeStampProperty($timeStampField = null) + { + $this->timeStampProperty = $timeStampField; + + return $this; + } + + /** + * Set the id generator to use. + * + * @param IdGeneratorInterface $idGenerator The id generator. + * + * @return DefaultDataProvider + */ + public function setIdGenerator($idGenerator) + { + $this->idGenerator = $idGenerator; + + return $this; + } + + /** + * Retrieve the id generator. + * + * @return IdGeneratorInterface + */ + public function getIdGenerator() + { + return $this->idGenerator; + } + + /** + * Create an instance of the default database driven uuid generator. + * + * @return DefaultDataProvider + * + * @throws DcGeneralRuntimeException When already an id generator has been set on the instance. + */ + public function enableDefaultUuidGenerator() + { + if ($this->idGenerator) { + throw new DcGeneralRuntimeException('Error: already an id generator set on database provider.'); + } + + $this->setIdGenerator(new DatabaseUuidIdGenerator($this->connection)); + + return $this; + } + + /** + * {@inheritDoc} + * + * @throws DcGeneralRuntimeException When no source has been defined. + * @throws DcGeneralRuntimeException For invalid database connection. + * + * @SuppressWarnings(PHPMD.CyclomaticComplexity) + * @SuppressWarnings(PHPMD.NPathComplexity) + */ + public function setBaseConfig(array $config) + { + // Check configuration. + if (!isset($config['source'])) { + throw new DcGeneralRuntimeException('Missing table name.'); + } + + $this->fallbackFromDatabaseToConnection($config); + + if (isset($config['connection'])) { + if (!($config['connection'] instanceof Connection)) { + throw new DcGeneralRuntimeException('Invalid database connection.'); + } + + $this->connection = $config['connection']; + } else { + // @codingStandardsIgnoreStart + @\trigger_error( + 'You should pass a doctrine database connection to "' . __METHOD__ . '".', + E_USER_DEPRECATED + ); + // @codingStandardsIgnoreEnd + + $this->connection = $this->getDefaultConnection(); + } + + if (isset($config['idGenerator'])) { + if ($config['idGenerator'] instanceof IdGeneratorInterface) { + $idGenerator = $config['idGenerator']; + $this->setIdGenerator($idGenerator); + } + } + + $this->source = $config['source']; + + if (isset($config['timeStampProperty'])) { + $this->setTimeStampProperty($config['timeStampProperty']); + } elseif ($this->fieldExists('tstamp')) { + $this->setTimeStampProperty('tstamp'); + } + + if (isset($config['idProperty'])) { + $this->setIdProperty($config['idProperty']); + } + } + + /** + * {@inheritDoc} + */ + public function getEmptyConfig() + { + return DefaultConfig::init(); + } + + /** + * {@inheritDoc} + */ + public function getEmptyModel() + { + $model = new DefaultModel(); + $model->setProviderName($this->source); + return $model; + } + + /** + * {@inheritDoc} + */ + public function getEmptyCollection() + { + return new DefaultCollection(); + } + + /** + * Fetch an empty single filter option collection (new model list). + * + * @return FilterOptionCollectionInterface + * + * @deprecated This method was never intended to be used externally. + */ + public function getEmptyFilterOptionCollection() + { + // @codingStandardsIgnoreStart + @\trigger_error( + 'Method ' . __METHOD__ . ' was never intended to be called via interface and will get removed', + E_USER_DEPRECATED + ); + // @codingStandardsIgnoreEnd + return new DefaultFilterOptionCollection(); + } + + /** + * {@inheritDoc} + * + * @throws DcGeneralRuntimeException When an unusable object has been passed. + */ + public function delete($item) + { + $modelId = null; + if (\is_numeric($item) || \is_string($item)) { + $modelId = $item; + } elseif (\is_object($item) && $item instanceof ModelInterface && null !== $item->getId()) { + $modelId = $item->getId(); + } else { + throw new DcGeneralRuntimeException("ID missing or given object not of type 'ModelInterface'."); + } + + // Insert undo. + $this->insertUndo( + \sprintf( + 'DELETE FROM %1$s WHERE %1$s.id = %2$s', + $this->source, + $modelId + ), + \sprintf( + 'SELECT * FROM %1$s WHERE %1$s.id = %2$s', + $this->source, + $modelId + ), + $this->source + ); + + $this->connection->delete($this->source, [$this->source . '.id' => $modelId]); + } + + /** + * Create a model from a database result. + * + * @param array $result The database result to create a model from. + * + * @return ModelInterface + */ + protected function createModelFromDatabaseResult(array $result) + { + $model = $this->getEmptyModel(); + + foreach ($result as $key => $value) { + if ($key === $this->idProperty) { + $model->setIdRaw($value); + } + + $model->setPropertyRaw($key, StringUtil::deserialize($value)); + } + + return $model; + } + + /** + * {@inheritDoc} + * + * @throws \Doctrine\DBAL\Exception + */ + public function fetch(ConfigInterface $config) + { + $queryBuilder = $this->connection->createQueryBuilder(); + $queryBuilder->from($this->source); + DefaultDataProviderDBalUtils::addField($config, $this->idProperty, $queryBuilder); + + if (null !== $config->getId()) { + $queryBuilder->where($this->source . '.id=:id'); + $queryBuilder->setParameter('id', $config->getId()); + } + + if (null === $config->getId()) { + DefaultDataProviderDBalUtils::addWhere($config, $queryBuilder); + DefaultDataProviderDBalUtils::addSorting($config, $queryBuilder); + + $queryBuilder->setMaxResults(1); + $queryBuilder->setFirstResult(0); + } + + $statement = $queryBuilder->executeQuery(); + if (0 === $statement->rowCount()) { + return null; + } + + $result = $statement->fetchAssociative(); + + return $this->createModelFromDatabaseResult($result); + } + + /** + * {@inheritDoc} + * + * @throws \Doctrine\DBAL\Exception + */ + public function fetchAll(ConfigInterface $config) + { + $queryBuilder = $this->connection->createQueryBuilder(); + $queryBuilder->from($this->source); + DefaultDataProviderDBalUtils::addField($config, $this->idProperty, $queryBuilder); + DefaultDataProviderDBalUtils::addWhere($config, $queryBuilder); + DefaultDataProviderDBalUtils::addSorting($config, $queryBuilder); + + if (0 !== $config->getAmount()) { + $queryBuilder->setMaxResults($config->getAmount()); + $queryBuilder->setFirstResult($config->getStart() ?? 0); + } + + $collection = $this->getEmptyCollection(); + + $statement = $queryBuilder->executeQuery(); + if (0 === $statement->rowCount()) { + return $collection; + } + + if ($config->getIdOnly()) { + return $statement->fetchFirstColumn(); + } + + $result = $statement->fetchAllAssociative(); + + foreach ($result as $item) { + $collection->push($this->createModelFromDatabaseResult($item)); + } + + return $collection; + } + + /** + * {@inheritDoc} + * + * @throws DcGeneralRuntimeException If improper values have been passed (i.e. not exactly one field requested). + * @throws \Doctrine\DBAL\Exception + */ + public function getFilterOptions(ConfigInterface $config) + { + $internalConfig = $this->prefixDataProviderProperties($config); + $properties = $internalConfig->getFields(); + if (1 !== \count($properties)) { + throw new DcGeneralRuntimeException('objConfig must contain exactly one property to be retrieved.'); + } + $property = $properties[0]; + + $queryBuilder = $this->connection->createQueryBuilder(); + $queryBuilder->select('DISTINCT(' . $property . ')'); + $queryBuilder->from($this->source); + DefaultDataProviderDBalUtils::addWhere($internalConfig, $queryBuilder); + + $statement = $queryBuilder->executeQuery(); + + $values = $statement->fetchAllAssociative(); + + $filterProperties = $config->getFields(); + if (1 !== \count($filterProperties)) { + throw new DcGeneralRuntimeException('objConfig must contain exactly one property to be retrieved.'); + } + $filterProperty = $filterProperties[0]; + + $collection = new DefaultFilterOptionCollection(); + foreach ($values as $value) { + $collection->add($value[$filterProperty], $value[$filterProperty]); + } + + return $collection; + } + + /** + * {@inheritDoc} + * + * @throws \Doctrine\DBAL\Exception + */ + public function getCount(ConfigInterface $config) + { + $internalConfig = $this->prefixDataProviderProperties($config); + $queryBuilder = $this->connection->createQueryBuilder(); + $queryBuilder->select('COUNT(*) AS count'); + $queryBuilder->from($this->source); + DefaultDataProviderDBalUtils::addWhere($internalConfig, $queryBuilder); + + $statement = $queryBuilder->executeQuery(); + + return $statement->fetchFirstColumn(); + } + + /** + * {@inheritDoc} + * @throws \Doctrine\DBAL\Exception + */ + public function isUniqueValue($field, $new, $primaryId = null) + { + $queryBuilder = $this->connection->createQueryBuilder(); + $queryBuilder->select('*'); + $queryBuilder->from($this->source); + $queryBuilder->where($queryBuilder->expr()->eq($this->source . '.' . $field, ':' . $field)); + $queryBuilder->setParameter($field, $new); + + $statement = $queryBuilder->executeQuery(); + $unique = $statement->fetchAssociative(); + + if (0 === $statement->rowCount()) { + return true; + } + + if (($primaryId === $unique['id']) && (1 === $statement->rowCount())) { + return true; + } + + return false; + } + + /** + * {@inheritDoc} + */ + public function resetFallback($field) + { + // @codingStandardsIgnoreStart + @\trigger_error( + __CLASS__ . '::' . __METHOD__ . ' is deprecated - handle resetting manually', + E_USER_DEPRECATED + ); + // @codingStandardsIgnoreEnd + + $this->connection->query('UPDATE ' . $this->source . ' SET ' . $field . ' = \'\'')->execute(); + } + + /** + * Prefix the data provider properties. + * + * @param ConfigInterface $config The config. + * + * @return ConfigInterface + */ + private function prefixDataProviderProperties(ConfigInterface $config) + { + $internalConfig = clone $config; + $this->sortingPrefixer($internalConfig); + + if (null !== ($filter = $internalConfig->getFilter())) { + $this->filterPrefixer($filter); + $internalConfig->setFilter($filter); + } + + if (null !== ($fields = $internalConfig->getFields())) { + $this->fieldPrefixer($fields); + $internalConfig->setFields($fields); + } + + return $internalConfig; + } + + /** + * The config sorting prefixer. + * + * @param ConfigInterface $config The config. + * + * @return void + */ + private function sortingPrefixer(ConfigInterface $config) + { + $sorting = []; + foreach ($config->getSorting() as $property => $value) { + if (0 === \strpos($property, $this->source . '.')) { + $sorting[$property] = $value; + + continue; + } + + if (!$this->fieldExists($property)) { + continue; + } + + $sorting[$this->source . '.' . $property] = $value; + } + $config->setSorting($sorting); + } + + /** + * The filter prefixer. + * + * @param array $filter The filter setting. + * + * @return void + */ + private function filterPrefixer(array &$filter) + { + foreach ($filter as &$child) { + if (\array_key_exists('property', $child) + && (false === \strpos($child['property'], $this->source . '.')) + && $this->fieldExists($child['property']) + ) { + $child['property'] = $this->source . '.' . $child['property']; + } + + if (\array_key_exists('children', $child)) { + $this->filterPrefixer($child['children']); + } + } + } + + /** + * The field prefixer. + * + * @param array $fields The fields. + * + * @return void + */ + private function fieldPrefixer(array &$fields) + { + foreach ($fields as $index => $property) { + if (0 === \strpos($property, $this->source . '.') + || !$this->fieldExists($property) + ) { + continue; + } + + $fields[$index] = $this->source . '.' . $property; + } + } + + /** + * Convert a model into a property array to be used in insert and update queries. + * + * @param ModelInterface $model The model to convert into an property array. + * @param int $timestamp Optional the timestamp. + * + * @return array + * + * @SuppressWarnings(PHPMD.Superglobals) + */ + private function convertModelToDataPropertyArray(ModelInterface $model, int $timestamp) + { + $data = []; + foreach ($model as $key => $value) { + if (($key === $this->idProperty) || !$this->fieldExists($key)) { + continue; + } + + if (\is_array($value)) { + $data[$this->source . '.' . $key] = \serialize($value); + } else { + $data[$this->source . '.' . $key] = $value; + } + } + + if ($this->timeStampProperty) { + $data[$this->source . '.' . $this->getTimeStampProperty()] = $timestamp ?: \time(); + } + + return $data; + } + + /** + * Insert the model into the database. + * + * @param ModelInterface $model The model to insert into the database. + * @param int $timestamp Optional the timestamp. + * + * @return void + */ + private function insertModelIntoDatabase(ModelInterface $model, $timestamp = 0) + { + $data = $this->convertModelToDataPropertyArray($model, $timestamp); + if ($this->getIdGenerator()) { + $model->setId($this->getIdGenerator()->generate()); + $data[$this->idProperty] = $model->getId(); + } + + $this->connection->insert($this->source, $data); + + $insertId = $this->connection->lastInsertId($this->source); + + if (('' !== $insertId) && !isset($data[$this->idProperty])) { + $model->setId($insertId); + } + } + + /** + * Update the model in the database. + * + * @param ModelInterface $model The model to update the database. + * @param int $timestamp Optional the timestamp. + * + * @return void + */ + private function updateModelInDatabase($model, $timestamp = 0) + { + $data = $this->convertModelToDataPropertyArray($model, $timestamp); + + $this->connection->update($this->source, $data, ['id' => $model->getId()]); + } + + /** + * {@inheritDoc} + */ + public function save(ModelInterface $item, $timestamp = 0) + { + if (\in_array($item->getId(), [null, ''])) { + $this->insertModelIntoDatabase($item); + } else { + $this->updateModelInDatabase($item); + } + + return $item; + } + + /** + * {@inheritDoc} + */ + public function saveEach(CollectionInterface $items, $timestamp = 0) + { + foreach ($items as $value) { + $this->save($value, $timestamp); + } + } + + /** + * {@inheritDoc} + */ + public function fieldExists($columnName) + { + if (!isset($this->schema)) { + $this->schema = $this->connection->getSchemaManager()->listTableDetails($this->source); + } + + return $this->schema->hasColumn($columnName); + } + + /** + * {@inheritDoc} + * + * @throws \Doctrine\DBAL\Exception + */ + public function getVersion($mixID, $mixVersion) + { + $queryBuilder = $this->connection->createQueryBuilder(); + $queryBuilder->select('*'); + $queryBuilder->from('tl_version'); + $queryBuilder->andWhere($queryBuilder->expr()->eq('tl_version.pid', ':pid')); + $queryBuilder->setParameter('pid', $mixID); + $queryBuilder->andWhere($queryBuilder->expr()->eq('tl_version.version', ':version')); + $queryBuilder->setParameter('version', $mixVersion); + $queryBuilder->andWhere($queryBuilder->expr()->eq('tl_version.fromTable', ':fromTable')); + $queryBuilder->setParameter('fromTable', $this->source); + + $statement = $queryBuilder->executeQuery(); + if (0 === $statement->rowCount()) { + return null; + } + + if (null === $version = $statement->fetchAllAssociative()) { + return null; + } + + $data = StringUtil::deserialize($version['data']); + + if (!\is_array($data) || (0 === \count($data))) { + return null; + } + + $model = $this->getEmptyModel(); + $model->setID($mixID); + foreach ($data as $key => $value) { + if ($key === $this->idProperty) { + continue; + } + + $model->setProperty($key, $value); + } + + return $model; + } + + /** + * Return a list with all versions for the row with the given Id. + * + * @param mixed $mixID The ID of the row. + * @param boolean $onlyActive If true, only active versions will get returned, if false all version will get + * returned. + * + * @return CollectionInterface + * + * @throws \Doctrine\DBAL\Exception + */ + public function getVersions($mixID, $onlyActive = false) + { + $queryBuilder = $this->connection->createQueryBuilder(); + $queryBuilder->select(['tstamp', 'version', 'username', 'active']); + $queryBuilder->from('tl_version'); + $queryBuilder->andWhere($queryBuilder->expr()->eq('tl_version.fromTable', ':fromTable')); + $queryBuilder->setParameter('fromTable', $this->source); + $queryBuilder->andWhere($queryBuilder->expr()->eq('tl_version.pid', ':pid')); + $queryBuilder->setParameter('pid', $mixID); + + if ($onlyActive) { + $queryBuilder->andWhere($queryBuilder->expr()->eq('tl_version.active', ':active')); + $queryBuilder->setParameter('active', '1'); + } else { + $queryBuilder->orderBy('tl_version.version', 'DESC'); + } + + $statement = $queryBuilder->executeQuery(); + if (0 === $statement->rowCount()) { + return null; + } + + $versions = $statement->fetchAllAssociative(); + + $collection = $this->getEmptyCollection(); + + foreach ((array) $versions as $versionValue) { + $model = $this->getEmptyModel(); + $model->setId($mixID); + + foreach ($versionValue as $key => $value) { + if ($key === $this->idProperty) { + continue; + } + + $model->setProperty($key, $value); + } + + $collection->push($model); + } + + return $collection; + } + + /** + * Save a new version of a row. + * + * @param ModelInterface $model The model for which a new version shall be created. + * @param string $username The username to attach to the version as creator. + * + * @return void + * + * @throws \Doctrine\DBAL\Exception + */ + public function saveVersion(ModelInterface $model, $username) + { + $queryBuilder = $this->connection->createQueryBuilder(); + $queryBuilder->select('COUNT(*) AS count'); + $queryBuilder->from('tl_version'); + $queryBuilder->andWhere($queryBuilder->expr()->eq('tl_version.pid', ':pid')); + $queryBuilder->setParameter('pid', $model->getId()); + $queryBuilder->andWhere($queryBuilder->expr()->eq('tl_version.fromTable', ':fromTable')); + $queryBuilder->setParameter('fromTable', $this->source); + + $statement = $queryBuilder->executeQuery(); + $count = $statement->fetchFirstColumn(); + + $mixNewVersion = ((int) $count + 1); + $mixData = $model->getPropertiesAsArray(); + + $mixData[$this->idProperty] = $model->getId(); + + $insert = [ + 'tl_version.pid' => $model->getId(), + 'tl_version.tstamp' => \time(), + 'tl_version.version' => $mixNewVersion, + 'tl_version.fromTable' => $this->source, + 'tl_version.username' => $username, + 'tl_version.data' => \serialize($mixData) + ]; + + $this->connection->insert('tl_version', $insert); + + $this->setVersionActive($model->getId(), $mixNewVersion); + } + + /** + * Set a version as active. + * + * @param mixed $mixID The ID of the row. + * @param mixed $mixVersion The version number to set active. + * + * @return void + */ + public function setVersionActive($mixID, $mixVersion) + { + $updateValues = ['tl_version.pid' => $mixID, 'tl_version.fromTable' => $this->source]; + + // Set version inactive. + $this->connection->update('tl_version', ['tl_version.active' => ''], $updateValues); + + // Set version active. + $updateValues['version'] = $mixVersion; + $this->connection->update('tl_version', ['tl_version.active' => 1], $updateValues); + } + + /** + * Retrieve the current active version for a row. + * + * @param mixed $mixID The ID of the row. + * + * @return mixed The current version number of the requested row. + * + * @throws \Doctrine\DBAL\Exception + */ + public function getActiveVersion($mixID) + { + $queryBuilder = $this->connection->createQueryBuilder(); + $queryBuilder->select('version'); + $queryBuilder->from('tl_version'); + $queryBuilder->andWhere($queryBuilder->expr()->eq('tl_version.pid', ':pid')); + $queryBuilder->setParameter('pid', $mixID); + $queryBuilder->andWhere($queryBuilder->expr()->eq('tl_version.fromTable', ':fromTable')); + $queryBuilder->setParameter('fromTable', $this->source); + $queryBuilder->andWhere($queryBuilder->expr()->eq('tl_version.active', ':active')); + $queryBuilder->setParameter('active', 1); + + $statement = $queryBuilder->executeQuery(); + if (0 === $statement->rowCount()) { + return null; + } + + return $statement->fetchAllAssociative()['version']; + } + + /** + * Check if two models have the same values in all properties. + * + * @param ModelInterface $firstModel The first model to compare. + * @param ModelInterface $secondModel The second model to compare. + * + * @return boolean True - If both models are same, false if not. + */ + public function sameModels($firstModel, $secondModel) + { + foreach ($firstModel as $key => $value) { + if ($key === $this->idProperty) { + continue; + } + + if (\is_array($value)) { + if (!\is_array($secondModel->getProperty($key))) { + return false; + } + + if (\serialize($value) !== \serialize($secondModel->getProperty($key))) { + return false; + } + } elseif ($value !== $secondModel->getProperty($key)) { + return false; + } + } + + return true; + } + + /** + * Store an undo entry in the table tl_undo. + * + * Currently, this only supports delete queries. + * + * @param string $sourceSQL The SQL used to perform the action to be undone. + * @param string $saveSQL The SQL query to retrieve the current entries. + * @param string $table The table to be affected by the action. + * + * @return void + * @throws \Doctrine\DBAL\Exception + */ + protected function insertUndo($sourceSQL, $saveSQL, $table) + { + // Load row. + $statement = $this->connection->executeQuery($saveSQL); + + // Check if we have a result. + if (0 === $statement->rowCount()) { + return; + } + + $result = $statement->fetchAssociative(); + + // Save information in array. + $parameters = []; + foreach ($result as $value) { + $parameters[$table][] = $value; + } + + $prefix = '(DC General)'; + $user = BackendUser::getInstance(); + + // Write into undo. + $this->connection->insert( + 'tl_undo', + [ + 'tl_undo.pid' => $user->id, + 'tl_undo.tstamp' => \time(), + 'tl_undo.fromTable' => $table, + 'tl_undo.query' => $prefix . $sourceSQL, + 'tl_undo.affectedRows' => \count($parameters[$table]), + 'tl_undo.data' => \serialize($parameters) + ] + ); + } + + /** + * Get the default connection for the database. + * + * @return Connection + */ + protected function getDefaultConnection() + { + return System::getContainer()->get('database_connection'); + } + + /** + * This is a fallback for get the connection instead of the old database. + * + * @param array $config The configuration. + * + * @return void + * + * @deprecated This method is deprecated since 2.1 and where removed in 3.0. The old database never used in future. + */ + private function fallbackFromDatabaseToConnection(array &$config) + { + if (isset($config['database'])) { + // @codingStandardsIgnoreStart + @\trigger_error( + 'Config key database is deprecated use instead connection. Fallback will be dropped.', + E_USER_DEPRECATED + ); + // @codingStandardsIgnoreEnd + + if (!isset($config['connection'])) { + $config['connection'] = $config['database']; + } + + unset($config['database']); + } + + if (isset($config['connection']) && $config['connection'] instanceof Database) { + // @codingStandardsIgnoreStart + @\trigger_error( + '"' . __METHOD__ . '" now accepts doctrine instances - ' . + 'passing Contao database instances is deprecated.', + E_USER_DEPRECATED + ); + // @codingStandardsIgnoreEnd + $reflection = new \ReflectionProperty(Database::class, 'resConnection'); + $reflection->setAccessible(true); + + $config['connection'] = $reflection->getValue($config['connection']); + } + } +} diff --git a/src/DataDefinition/Definition/Properties/DefaultProperty.php b/src/DataDefinition/Definition/Properties/DefaultProperty.php index 3bfe722d..afb82280 100644 --- a/src/DataDefinition/Definition/Properties/DefaultProperty.php +++ b/src/DataDefinition/Definition/Properties/DefaultProperty.php @@ -25,6 +25,8 @@ * Class DefaultProperty. * * Default implementation of a property definition. + * + * @SuppressWarnings(PHPMD.TooManyFields) */ class DefaultProperty implements PropertyInterface, EmptyValueAwarePropertyInterface { diff --git a/src/DataDefinition/Definition/View/DefaultListingConfig.php b/src/DataDefinition/Definition/View/DefaultListingConfig.php index c757b666..2a3e7329 100644 --- a/src/DataDefinition/Definition/View/DefaultListingConfig.php +++ b/src/DataDefinition/Definition/View/DefaultListingConfig.php @@ -27,6 +27,8 @@ * Class DefaultListingConfig. * * Default implementation of a listing config. + * + * @SuppressWarnings(PHPMD.LongVariable) */ class DefaultListingConfig implements ListingConfigInterface { diff --git a/src/DataDefinition/ModelRelationship/ParentChildCondition.php b/src/DataDefinition/ModelRelationship/ParentChildCondition.php index b97f6a07..0acec112 100644 --- a/src/DataDefinition/ModelRelationship/ParentChildCondition.php +++ b/src/DataDefinition/ModelRelationship/ParentChildCondition.php @@ -29,6 +29,8 @@ /** * Default implementation of a parent child relationship. + * + * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) */ class ParentChildCondition extends AbstractCondition implements ParentChildConditionInterface { diff --git a/src/DataDefinition/Palette/Builder/PaletteBuilder.php b/src/DataDefinition/Palette/Builder/PaletteBuilder.php index 52ee2256..2e9dba15 100644 --- a/src/DataDefinition/Palette/Builder/PaletteBuilder.php +++ b/src/DataDefinition/Palette/Builder/PaletteBuilder.php @@ -83,6 +83,8 @@ * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) * @SuppressWarnings(PHPMD.TooManyPublicMethods) * @SuppressWarnings(PHPMD.ExcessiveClassLength) + * @SuppressWarnings(PHPMD.TooManyFields) + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class PaletteBuilder { diff --git a/src/DefaultEnvironment.php b/src/DefaultEnvironment.php index d88ade2e..7a4d8c8b 100644 --- a/src/DefaultEnvironment.php +++ b/src/DefaultEnvironment.php @@ -32,6 +32,8 @@ /** * Default implementation of an environment. + * + * @SuppressWarnings(PHPMD.LongVariable) */ class DefaultEnvironment implements EnvironmentInterface { diff --git a/src/Factory/DcGeneralFactory.php b/src/Factory/DcGeneralFactory.php index 9ea1bda6..1fe0e6f7 100644 --- a/src/Factory/DcGeneralFactory.php +++ b/src/Factory/DcGeneralFactory.php @@ -41,6 +41,8 @@ /** * Factory to create a DcGeneral instance. + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class DcGeneralFactory implements DcGeneralFactoryInterface { diff --git a/src/View/ActionHandler/AbstractPropertyOverrideEditAllHandler.php b/src/View/ActionHandler/AbstractPropertyOverrideEditAllHandler.php index 213c6477..75fa8961 100644 --- a/src/View/ActionHandler/AbstractPropertyOverrideEditAllHandler.php +++ b/src/View/ActionHandler/AbstractPropertyOverrideEditAllHandler.php @@ -43,6 +43,9 @@ /** * This class is the abstract base for override/edit all "overrideAll/editAll" commands. + * + * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ abstract class AbstractPropertyOverrideEditAllHandler extends AbstractPropertyVisibilityHandler { @@ -88,6 +91,8 @@ protected function handleSubmit(Action $action, EnvironmentInterface $environmen * @param EnvironmentInterface $environment The environment. * * @return void + * + * @SuppressWarnings(PHPMD.LongVariable) */ protected function editCollection( Action $action, @@ -158,6 +163,8 @@ protected function updateErrorInformation( * @param EnvironmentInterface $environment The environment. * * @return PropertyValueBagInterface + * + * @SuppressWarnings(PHPMD.LongVariable) */ private function cloneCleanPropertyValueBag( Action $action, @@ -273,44 +280,44 @@ function ($error) use ( /** * Update property value bag. * - * @param Action $action The action. - * @param ModelInterface $model The model. - * @param PropertyValueBagInterface $sourcePropertyValueBag The source property value bag. - * @param PropertyValueBagInterface $updatePropertyValueBag The update property value bag. - * @param EnvironmentInterface $environment The environment. + * @param Action $action The action. + * @param ModelInterface $model The model. + * @param PropertyValueBagInterface $sourceBag The source property value bag. + * @param PropertyValueBagInterface $updateBag The update property value bag. + * @param EnvironmentInterface $environment The environment. * * @return void */ private function updatePropertyValueBag( Action $action, ModelInterface $model, - PropertyValueBagInterface $sourcePropertyValueBag, - PropertyValueBagInterface $updatePropertyValueBag, + PropertyValueBagInterface $sourceBag, + PropertyValueBagInterface $updateBag, EnvironmentInterface $environment ) { $dataProvider = $environment->getDataProvider(); $sessionProperties = $this->getPropertiesFromSession($action, $environment); foreach (\array_keys($sessionProperties) as $sessionPropertyName) { - if (!$sourcePropertyValueBag->hasPropertyValue($sessionPropertyName)) { + if (!$sourceBag->hasPropertyValue($sessionPropertyName)) { continue; } - if (!$updatePropertyValueBag->isPropertyValueInvalid($sessionPropertyName)) { + if (!$updateBag->isPropertyValueInvalid($sessionPropertyName)) { $editModel = $dataProvider->fetch($dataProvider->getEmptyConfig()->setId($model->getId())); - $updatePropertyValueBag->setPropertyValue( + $updateBag->setPropertyValue( $sessionPropertyName, $editModel->getProperty($sessionPropertyName) ); } - if ($updatePropertyValueBag->isPropertyValueInvalid($sessionPropertyName)) { + if ($updateBag->isPropertyValueInvalid($sessionPropertyName)) { continue; } - $updatePropertyValueBag->markPropertyValueAsInvalid( + $updateBag->markPropertyValueAsInvalid( $sessionPropertyName, - $sourcePropertyValueBag->getPropertyValueErrors( + $sourceBag->getPropertyValueErrors( $sessionPropertyName ) ); diff --git a/src/View/ActionHandler/AbstractPropertyVisibilityHandler.php b/src/View/ActionHandler/AbstractPropertyVisibilityHandler.php index f481d6d7..aec6f75c 100644 --- a/src/View/ActionHandler/AbstractPropertyVisibilityHandler.php +++ b/src/View/ActionHandler/AbstractPropertyVisibilityHandler.php @@ -38,6 +38,7 @@ * This abstract visibility handler provide methods for the visibility of properties. * * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ abstract class AbstractPropertyVisibilityHandler { @@ -247,6 +248,8 @@ protected function ensurePropertyVisibleInModel( * @param EnvironmentInterface $environment The environment. * * @return bool + * + * @SuppressWarnings(PHPMD.LongVariable) */ private function matchVisibilityOfPropertyInAnyPalette( Action $action, @@ -297,6 +300,8 @@ private function matchVisibilityOfPropertyInAnyPalette( * @param EnvironmentInterface $environment The environment. * * @return bool + * + * @SuppressWarnings(PHPMD.LongVariable) */ private function matchPaletteProperty( PropertyInterface $property, @@ -713,6 +718,8 @@ protected function getIntersectionModel(Action $action, EnvironmentInterface $en * @param PaletteInterface|null $defaultPalette The default palette. * * @return bool + * + * @SuppressWarnings(PHPMD.LongVariable) */ private function useIntersectValue( $intersectPropertyName, diff --git a/tests/BaseConfigRegistryTest.php b/tests/BaseConfigRegistryTest.php index 272da960..88ea35ad 100644 --- a/tests/BaseConfigRegistryTest.php +++ b/tests/BaseConfigRegistryTest.php @@ -43,6 +43,8 @@ * Test the base configuration registry. * * @covers \ContaoCommunityAlliance\DcGeneral\BaseConfigRegistry + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class BaseConfigRegistryTest extends TestCase { @@ -59,6 +61,10 @@ public function testSetterAndGetter() self::assertSame($environment, $configRegistry->getEnvironment()); } + /** + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + * @SuppressWarnings(PHPMD.LongVariable) + */ public function testGetBaseConfig() { // Common test settings. @@ -193,6 +199,7 @@ function ($parentProviderName) use ($parentChildCondition) { self::assertSame($exceptedFilterWithChildCondition, $singleDataProviderConfig->getFilter()); } + /** @SuppressWarnings(PHPMD.LongVariable) */ public function testGetBaseConfigParentListMode() { $basicDefinition = diff --git a/tests/Cache/Http/InvalidateCacheTagsTest.php b/tests/Cache/Http/InvalidateCacheTagsTest.php index a4b74192..3cf131fb 100644 --- a/tests/Cache/Http/InvalidateCacheTagsTest.php +++ b/tests/Cache/Http/InvalidateCacheTagsTest.php @@ -42,6 +42,8 @@ * @covers \ContaoCommunityAlliance\DcGeneral\Cache\Http\InvalidateCacheTags * @covers \ContaoCommunityAlliance\DcGeneral\Event\InvalidHttpCacheTagsEvent * @covers \ContaoCommunityAlliance\DcGeneral\Controller\ModelCollector + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class InvalidateCacheTagsTest extends TestCase { @@ -168,6 +170,7 @@ function (array $invalidTags) use (&$actualInvalidTags) { self::assertSame(['namespace.foo', 'namespace.foo.1', 'namespace.foo.2'], $actualInvalidTags); } + /** @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ public function testPurgeHttpCacheWithParentNotHierarchical(): void { $dispatcher = $this->createMock(EventDispatcherInterface::class); @@ -274,6 +277,7 @@ function (array $invalidTags) use (&$actualInvalidTags) { self::assertSame(['namespace.foo', 'namespace.foo.1', 'namespace.bar', 'namespace.bar.2'], $actualInvalidTags); } + /** @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ public function testPurgeHttpCacheWithParentHierarchical(): void { $dispatcher = $this->createMock(EventDispatcherInterface::class); diff --git a/tests/Clipboard/FilterTest.php b/tests/Clipboard/FilterTest.php index 2354524d..8eb26009 100644 --- a/tests/Clipboard/FilterTest.php +++ b/tests/Clipboard/FilterTest.php @@ -31,6 +31,9 @@ * Test for the Filter. * * @covers \ContaoCommunityAlliance\DcGeneral\Clipboard\Filter + * + * @SuppressWarnings(PHPMD.TooManyMethods) + * @SuppressWarnings(PHPMD.TooManyPublicMethods) */ class FilterTest extends TestCase { diff --git a/tests/Clipboard/MockedAbstractItem.php b/tests/Clipboard/MockedAbstractItem.php index 7e5c89b8..ee4e14ad 100644 --- a/tests/Clipboard/MockedAbstractItem.php +++ b/tests/Clipboard/MockedAbstractItem.php @@ -51,6 +51,8 @@ class MockedAbstractItem extends AbstractItem * @param string $action The clipboard action name. * @param ModelIdInterface|null $parentId The parent id. * @param ModelIdInterface|string|null $modelIdOrProviderName The model id or provider name. + * + * @SuppressWarnings(PHPMD.LongVariable) */ public function __construct($action, ModelIdInterface $parentId = null, $modelIdOrProviderName = null) { diff --git a/tests/Contao/Callback/AbstractCallbackListenerTest.php b/tests/Contao/Callback/AbstractCallbackListenerTest.php index 565f07bd..c0d100d3 100644 --- a/tests/Contao/Callback/AbstractCallbackListenerTest.php +++ b/tests/Contao/Callback/AbstractCallbackListenerTest.php @@ -35,6 +35,7 @@ */ class AbstractCallbackListenerTest extends TestCase { + /** @SuppressWarnings(PHPMD.UnusedFormalParameter) - phpmd can not handle the use syntax. */ protected function getCallback($value) { return function () use ($value) { diff --git a/tests/Contao/Callback/AbstractContainerCallbackListenerTest.php b/tests/Contao/Callback/AbstractContainerCallbackListenerTest.php index d57cc3ec..ce24ed58 100644 --- a/tests/Contao/Callback/AbstractContainerCallbackListenerTest.php +++ b/tests/Contao/Callback/AbstractContainerCallbackListenerTest.php @@ -86,9 +86,12 @@ * @covers \ContaoCommunityAlliance\DcGeneral\Contao\Callback\ModelOperationButtonCallbackListener::wantToExecute * @covers \ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\GetOperationButtonEvent::getEnvironment * @covers \ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\GetOperationButtonEvent::getKey + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class AbstractContainerCallbackListenerTest extends TestCase { + /** @SuppressWarnings(PHPMD.UnusedFormalParameter) - phpmd can not handle the use syntax. */ protected function getCallback($value) { return function () use($value) { diff --git a/tests/Contao/Callback/AbstractReturningPropertyCallbackListenerTest.php b/tests/Contao/Callback/AbstractReturningPropertyCallbackListenerTest.php index bdbdf819..abe059f5 100644 --- a/tests/Contao/Callback/AbstractReturningPropertyCallbackListenerTest.php +++ b/tests/Contao/Callback/AbstractReturningPropertyCallbackListenerTest.php @@ -56,9 +56,13 @@ * @covers \ContaoCommunityAlliance\DcGeneral\Contao\Callback\PropertyInputFieldGetXLabelCallbackListener::wantToExecute * @covers \ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\ManipulateWidgetEvent::getEnvironment * @covers \ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\ManipulateWidgetEvent::getProperty + * + * @SuppressWarnings(PHPMD.LongClassName) + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class AbstractReturningPropertyCallbackListenerTest extends TestCase { + /** @SuppressWarnings(PHPMD.UnusedFormalParameter) - phpmd can not handle the use syntax. */ protected function getCallback($value) { return function () use($value) { diff --git a/tests/Contao/Event/SubscriberTest.php b/tests/Contao/Event/SubscriberTest.php index 56bf4795..2d656387 100644 --- a/tests/Contao/Event/SubscriberTest.php +++ b/tests/Contao/Event/SubscriberTest.php @@ -62,9 +62,14 @@ * This class test the subscriber. * * @covers \ContaoCommunityAlliance\DcGeneral\Contao\Event\Subscriber + * + * @SuppressWarnings(PHPMD.TooManyMethods) + * @SuppressWarnings(PHPMD.TooManyPublicMethods) + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class SubscriberTest extends TestCase { + /** @SuppressWarnings(PHPMD.Superglobals) */ public static function setUpBeforeClass(): void { $GLOBALS['TL_CONFIG']['characterSet'] = 'utf-8'; diff --git a/tests/Contao/View/Contao2BackendView/Subscriber/CheckPermissionTest.php b/tests/Contao/View/Contao2BackendView/Subscriber/CheckPermissionTest.php index 9a2e5ccc..e291ad43 100644 --- a/tests/Contao/View/Contao2BackendView/Subscriber/CheckPermissionTest.php +++ b/tests/Contao/View/Contao2BackendView/Subscriber/CheckPermissionTest.php @@ -37,6 +37,8 @@ /** * This tests the CheckPermission subscriber. + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class CheckPermissionTest extends TestCase { diff --git a/tests/Controller/ModelCollectorTest.php b/tests/Controller/ModelCollectorTest.php index ea9d7f43..d30b51b9 100644 --- a/tests/Controller/ModelCollectorTest.php +++ b/tests/Controller/ModelCollectorTest.php @@ -50,6 +50,9 @@ * Test case for the relationship manager. * * @covers \ContaoCommunityAlliance\DcGeneral\Controller\ModelCollector + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * @SuppressWarnings(PHPMD.TooManyPublicMethods) */ class ModelCollectorTest extends TestCase { @@ -134,12 +137,12 @@ public function testGetModel($modelId, $providerName) // Test with parent definition if (false !== \strpos(\is_object($modelId) ? $modelId->getSerialized() : $modelId, '::')) { - $parentPropertiesDefinition = $this->mockPropertiesDefinition(); - $parentPropertiesDefinition->method('getPropertyNames')->willReturn(['test-parent-property']); + $parentProperties = $this->mockPropertiesDefinition(); + $parentProperties->method('getPropertyNames')->willReturn(['test-parent-property']); $parentDataDefinition = $this->mockDefinitionContainer(); $parentDataDefinition->method('getName')->willReturn(ModelId::fromSerialized( \is_object($modelId) ? $modelId->getSerialized() : $modelId)->getDataProviderName()); - $parentDataDefinition->method('getPropertiesDefinition')->willReturn($parentPropertiesDefinition); + $parentDataDefinition->method('getPropertiesDefinition')->willReturn($parentProperties); $environment->method('getParentDataDefinition')->willReturn($parentDataDefinition); } diff --git a/tests/Data/DefaultDataProviderTest.php b/tests/Data/DefaultDataProviderTest.php index fa45c706..92dc82e2 100644 --- a/tests/Data/DefaultDataProviderTest.php +++ b/tests/Data/DefaultDataProviderTest.php @@ -37,6 +37,8 @@ * This class tests the DefaultDataProvider class. * * @covers \ContaoCommunityAlliance\DcGeneral\Data\DefaultDataProvider + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class DefaultDataProviderTest extends TestCase { diff --git a/tests/DcGeneralTest.php b/tests/DcGeneralTest.php index 6497c687..edec1e76 100644 --- a/tests/DcGeneralTest.php +++ b/tests/DcGeneralTest.php @@ -68,19 +68,19 @@ public function testInstantiation() } }); - $mockDefinitionContainer = $this->getMockForAbstractClass(DataDefinitionContainerInterface::class); - $mockDefinitionContainer + $definitionContainer = $this->getMockForAbstractClass(DataDefinitionContainerInterface::class); + $definitionContainer ->method('hasDefinition') ->willReturn(false); System::setContainer($container = $this->getMockForAbstractClass(ContainerInterface::class)); $container ->method('get') - ->willReturnCallback(function ($name) use ($eventDispatcher, $mockDefinitionContainer) { + ->willReturnCallback(function ($name) use ($eventDispatcher, $definitionContainer) { switch ($name) { case 'event_dispatcher': return $eventDispatcher; case 'cca.translator.contao_translator': return new StaticTranslator(); - case 'cca.dc-general.data-definition-container': return $mockDefinitionContainer; + case 'cca.dc-general.data-definition-container': return $definitionContainer; } return null; }); diff --git a/tests/DefaultEnvironmentTest.php b/tests/DefaultEnvironmentTest.php index 82d0cbc9..f57c79b7 100644 --- a/tests/DefaultEnvironmentTest.php +++ b/tests/DefaultEnvironmentTest.php @@ -39,6 +39,8 @@ * Test all methods for the default environment. * * @covers \ContaoCommunityAlliance\DcGeneral\DefaultEnvironment + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class DefaultEnvironmentTest extends TestCase { diff --git a/tests/Factory/DcGeneralFactoryTest.php b/tests/Factory/DcGeneralFactoryTest.php index 17ee655c..433b9713 100644 --- a/tests/Factory/DcGeneralFactoryTest.php +++ b/tests/Factory/DcGeneralFactoryTest.php @@ -49,14 +49,14 @@ public function testCreateDcGeneral() System::setContainer($container = $this->getMockForAbstractClass(ContainerInterface::class)); - $mockDefinitionContainer = $this->getMockForAbstractClass(DataDefinitionContainerInterface::class); + $definitionContainer = $this->getMockForAbstractClass(DataDefinitionContainerInterface::class); $container ->expects(self::once()) ->method('get') ->with('cca.dc-general.data-definition-container') - ->willReturn($mockDefinitionContainer); + ->willReturn($definitionContainer); - $mockDefinitionContainer + $definitionContainer ->expects(self::once()) ->method('hasDefinition') ->with('test-container') diff --git a/tests/Fixtures/Contao/Config.php b/tests/Fixtures/Contao/Config.php index 37ee16d6..1363407f 100644 --- a/tests/Fixtures/Contao/Config.php +++ b/tests/Fixtures/Contao/Config.php @@ -52,6 +52,8 @@ public static function getInstance() * @param string $strKey The short key (e.g. "displayErrors") * * @return mixed|null The configuration value + * + * @SuppressWarnings(PHPMD.Superglobals) */ public static function get($strKey) { diff --git a/tests/TestCase.php b/tests/TestCase.php index 495d4736..b8ca9776 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -29,6 +29,9 @@ /** * Base TestCase class. + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * @SuppressWarnings(PHPMD.NumberOfChildren) */ abstract class TestCase extends \PHPUnit\Framework\TestCase { From 8b541fa1f45485e1e875453a1d61a7e6aa164747 Mon Sep 17 00:00:00 2001 From: Christian Schiffler Date: Mon, 31 Oct 2022 16:50:55 +0100 Subject: [PATCH 10/53] Bump tools and remove duplicate execution of composer-normalize --- .phpcq.lock | 2 +- .phpcq.yaml.dist | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/.phpcq.lock b/.phpcq.lock index d3af6d72..21bdc05d 100644 --- a/.phpcq.lock +++ b/.phpcq.lock @@ -1 +1 @@ -{"plugins":{"phpunit":{"api-version":"1.0.0","version":"1.0.0.0","type":"php-file","url":"https://phpcq.github.io/repository/phpunit-1.0.0.0.php","signature":null,"requirements":{"php":{"php":"^7.3 || ^8.0"},"tool":{"phpunit":"^6.0 || ^7.0 || ^8.0 || ^9.0"}},"checksum":{"type":"sha-512","value":"c73f15658e3ba62665f09492ec91c3a6a715760bfaa88473a987538439fff442540148e086e46a6aa18ce55a3ea2fbf76caaa581384cb84a38859fcc609ae7e4"},"tools":{"phpunit":{"version":"9.5.25","url":"https://phar.phpunit.de/phpunit-9.5.25.phar","requirements":{"php":{"php":">=7.3","ext-dom":"*","ext-json":"*","ext-libxml":"*","ext-mbstring":"*","ext-xml":"*","ext-xmlwriter":"*"}},"checksum":{"type":"sha-256","value":"9b17aaed7c447841dab0ec12c380071f9ab43dd1e4ee9fbc319dd47282a2b4be"},"signature":"https://phar.phpunit.de/phpunit-9.5.25.phar.asc"}},"composerLock":null},"psalm":{"api-version":"1.0.0","version":"1.1.1.0","type":"php-file","url":"https://phpcq.github.io/repository/psalm-1.1.1.0.php","signature":null,"requirements":{"php":{"php":"^7.3 || ^8.0","ext-dom":"*"},"tool":{"psalm":"^3.0 || ^4.0"}},"checksum":{"type":"sha-512","value":"4a550c9226d7bca582d7c10bd87cce01190c96398936b1613421640c83df62ed1c6e0d44c1b39635414ea8cf4a892a6458d27590793238add24e7cb5547e6ffd"},"tools":{"psalm":{"version":"4.28.0","url":"https://github.com/vimeo/psalm/releases/download/4.28.0/psalm.phar","requirements":{"php":{"php":"^7.1|^8","ext-SimpleXML":"*","ext-ctype":"*","ext-dom":"*","ext-json":"*","ext-libxml":"*","ext-mbstring":"*","ext-tokenizer":"*"}},"checksum":null,"signature":"https://github.com/vimeo/psalm/releases/download/4.28.0/psalm.phar.asc"}},"composerLock":null},"composer-require-checker":{"api-version":"1.0.0","version":"1.1.1.0","type":"php-file","url":"https://phpcq.github.io/repository/composer-require-checker-1.1.1.0.php","signature":null,"requirements":{"php":{"php":"^7.4 || ^8.0"},"tool":{"composer-require-checker":"^3.8 || ^4.0"}},"checksum":{"type":"sha-512","value":"d5415bddfe024c5749d894034583882aee4e5c3e1087815d9fdd81cb5e71630f631a0e35de0ff84b97fbbf738c16ece5f83bd8c00695913eb846aa6f04577dc2"},"tools":{"composer-require-checker":{"version":"3.8.0","url":"https://github.com/maglnet/ComposerRequireChecker/releases/download/3.8.0/composer-require-checker.phar","requirements":{"php":{"php":"^7.4 || ^8.0","ext-json":"*","ext-phar":"*"}},"checksum":null,"signature":"https://github.com/maglnet/ComposerRequireChecker/releases/download/3.8.0/composer-require-checker.phar.asc"}},"composerLock":null},"phpmd":{"api-version":"1.0.0","version":"1.0.2.0","type":"php-file","url":"https://phpcq.github.io/repository/phpmd-1.0.2.0.php","signature":null,"requirements":{"php":{"php":"^7.3 || ^8.0","ext-dom":"*"},"tool":{"phpmd":"^2.6.1"}},"checksum":{"type":"sha-512","value":"f22280a6dec8dbdd2ec1d83b294f23237fe32c34f4a298e52038e0a7a0074d541635b2b488b1a6098a42d8418a6cd8eb804406ea82b91e362be2b5d11a0915b0"},"tools":{"phpmd":{"version":"2.13.0","url":"https://github.com/phpmd/phpmd/releases/download/2.13.0/phpmd.phar","requirements":{"php":{"php":">=5.3.9","ext-xml":"*"}},"checksum":null,"signature":"https://github.com/phpmd/phpmd/releases/download/2.13.0/phpmd.phar.asc"}},"composerLock":null},"phpcpd":{"api-version":"1.0.0","version":"1.1.1.0","type":"php-file","url":"https://phpcq.github.io/repository/phpcpd-1.1.1.0.php","signature":null,"requirements":{"php":{"php":"^7.3 || ^8.0","ext-dom":"*"},"tool":{"phpcpd":"^6.0"}},"checksum":{"type":"sha-512","value":"1189ce0bf3fade4cb4241f1d96f915ef8fc7651f4450dc79fdf464ee3d6be3009316f0d423ce2d4af9d76ad50807b7fdf4d77bfa6d9ee2c91d6eda32ea214433"},"tools":{"phpcpd":{"version":"6.0.3","url":"https://phar.phpunit.de/phpcpd-6.0.3.phar","requirements":{"php":{"php":">=7.3","ext-dom":"*"}},"checksum":{"type":"sha-256","value":"2cbaea7cfda1bb4299d863eb075e977c3f49055dd16d88529fae5150d48a84cb"},"signature":"https://phar.phpunit.de/phpcpd-6.0.3.phar.asc"}},"composerLock":null},"phploc":{"api-version":"1.0.0","version":"1.0.0.0","type":"php-file","url":"https://phpcq.github.io/repository/phploc-1.0.0.0.php","signature":null,"requirements":{"php":{"php":"^7.3 || ^8.0","ext-dom":"*","ext-json":"*"},"tool":{"phploc":"^3.0 || ^4.0 || ^5.0 || ^6.0 || ^7.0"}},"checksum":{"type":"sha-512","value":"f67b02d494796adf553cb3dd13ec06c1cb8e53c799954061749424251379541637538199afb3afa3c7a01cabd1cb6f1c53eb621f015dff9644c6c7cbf10c56d1"},"tools":{"phploc":{"version":"7.0.2","url":"https://phar.phpunit.de/phploc-7.0.2.phar","requirements":{"php":{"php":">=7.3","ext-dom":"*","ext-json":"*"}},"checksum":{"type":"sha-256","value":"3d59778ec86faf25fd00e3a329b2f9ad4a3c751ca91601ea7dab70f887b0bf46"},"signature":"https://phar.phpunit.de/phploc-7.0.2.phar.asc"}},"composerLock":null},"phpcs":{"api-version":"1.0.0","version":"1.1.0.0","type":"php-file","url":"https://phpcq.github.io/repository/phpcs-1.1.0.0.php","signature":null,"requirements":{"php":{"php":"^7.3 || ^8.0","ext-dom":"*"},"tool":{"phpcs":"^3.0 || ^2.0","phpcbf":"^3.0 || ^2.0"}},"checksum":{"type":"sha-512","value":"2737022369da1318cc4e0ea194e8a81019f7b079080d869aab878b7486052fdbe68fee3f28131f35573226def1aabd4bd005e038ee7b767c137b1107c1492a83"},"tools":{"phpcs":{"version":"3.7.1","url":"https://github.com/squizlabs/PHP_CodeSniffer/releases/download/3.7.1/phpcs.phar","requirements":{"php":{"php":">=5.4.0","ext-tokenizer":"*","ext-xmlwriter":"*","ext-simplexml":"*"}},"checksum":null,"signature":"https://github.com/squizlabs/PHP_CodeSniffer/releases/download/3.7.1/phpcs.phar.asc"},"phpcbf":{"version":"3.7.1","url":"https://github.com/squizlabs/PHP_CodeSniffer/releases/download/3.7.1/phpcbf.phar","requirements":{"php":{"php":">=5.4.0","ext-tokenizer":"*","ext-xmlwriter":"*","ext-simplexml":"*"}},"checksum":null,"signature":"https://github.com/squizlabs/PHP_CodeSniffer/releases/download/3.7.1/phpcbf.phar.asc"}},"composerLock":null},"composer-normalize":{"api-version":"1.0.0","version":"1.1.0.0","type":"php-file","url":"https://phpcq.github.io/repository/composer-normalize-1.1.0.0.php","signature":null,"requirements":{"php":{"php":"^7.3 || ^8.0","ext-json":"*"},"tool":{"composer-normalize":"^2.1"}},"checksum":{"type":"sha-512","value":"d59d3557cb20630734878a9115df5dd32d5aff815e5b15be36f6fb5d6e9d83dd36efd84215ab6529edcc924f600946f739a0d9e67723deff95c88346ab502498"},"tools":{"composer-normalize":{"version":"2.28.3","url":"https://github.com/ergebnis/composer-normalize/releases/download/2.28.3/composer-normalize.phar","requirements":{"php":{"php":"^7.4 || ^8.0"}},"checksum":null,"signature":"https://github.com/ergebnis/composer-normalize/releases/download/2.28.3/composer-normalize.phar.asc"}},"composerLock":null}},"tools":[]} \ No newline at end of file +{"plugins":{"phpunit":{"api-version":"1.0.0","version":"1.0.0.0","type":"php-file","url":"https://phpcq.github.io/repository/phpunit-1.0.0.0.php","signature":null,"requirements":{"php":{"php":"^7.3 || ^8.0"},"tool":{"phpunit":"^6.0 || ^7.0 || ^8.0 || ^9.0"}},"checksum":{"type":"sha-512","value":"c73f15658e3ba62665f09492ec91c3a6a715760bfaa88473a987538439fff442540148e086e46a6aa18ce55a3ea2fbf76caaa581384cb84a38859fcc609ae7e4"},"tools":{"phpunit":{"version":"9.5.26","url":"https://phar.phpunit.de/phpunit-9.5.26.phar","requirements":{"php":{"php":">=7.3","ext-dom":"*","ext-json":"*","ext-libxml":"*","ext-mbstring":"*","ext-xml":"*","ext-xmlwriter":"*"}},"checksum":{"type":"sha-256","value":"e36595f47bb81f244e03fc437328cfaa123e99ffd183c3217235926ff0978397"},"signature":"https://phar.phpunit.de/phpunit-9.5.26.phar.asc"}},"composerLock":null},"psalm":{"api-version":"1.0.0","version":"1.1.1.0","type":"php-file","url":"https://phpcq.github.io/repository/psalm-1.1.1.0.php","signature":null,"requirements":{"php":{"php":"^7.3 || ^8.0","ext-dom":"*"},"tool":{"psalm":"^3.0 || ^4.0"}},"checksum":{"type":"sha-512","value":"4a550c9226d7bca582d7c10bd87cce01190c96398936b1613421640c83df62ed1c6e0d44c1b39635414ea8cf4a892a6458d27590793238add24e7cb5547e6ffd"},"tools":{"psalm":{"version":"4.29.0","url":"https://github.com/vimeo/psalm/releases/download/4.29.0/psalm.phar","requirements":{"php":{"php":"^7.1|^8","ext-SimpleXML":"*","ext-ctype":"*","ext-dom":"*","ext-json":"*","ext-libxml":"*","ext-mbstring":"*","ext-tokenizer":"*"}},"checksum":null,"signature":"https://github.com/vimeo/psalm/releases/download/4.29.0/psalm.phar.asc"}},"composerLock":null},"composer-require-checker":{"api-version":"1.0.0","version":"1.1.1.0","type":"php-file","url":"https://phpcq.github.io/repository/composer-require-checker-1.1.1.0.php","signature":null,"requirements":{"php":{"php":"^7.4 || ^8.0"},"tool":{"composer-require-checker":"^3.8 || ^4.0"}},"checksum":{"type":"sha-512","value":"d5415bddfe024c5749d894034583882aee4e5c3e1087815d9fdd81cb5e71630f631a0e35de0ff84b97fbbf738c16ece5f83bd8c00695913eb846aa6f04577dc2"},"tools":{"composer-require-checker":{"version":"3.8.0","url":"https://github.com/maglnet/ComposerRequireChecker/releases/download/3.8.0/composer-require-checker.phar","requirements":{"php":{"php":"^7.4 || ^8.0","ext-json":"*","ext-phar":"*"}},"checksum":null,"signature":"https://github.com/maglnet/ComposerRequireChecker/releases/download/3.8.0/composer-require-checker.phar.asc"}},"composerLock":null},"phpmd":{"api-version":"1.0.0","version":"1.0.2.0","type":"php-file","url":"https://phpcq.github.io/repository/phpmd-1.0.2.0.php","signature":null,"requirements":{"php":{"php":"^7.3 || ^8.0","ext-dom":"*"},"tool":{"phpmd":"^2.6.1"}},"checksum":{"type":"sha-512","value":"f22280a6dec8dbdd2ec1d83b294f23237fe32c34f4a298e52038e0a7a0074d541635b2b488b1a6098a42d8418a6cd8eb804406ea82b91e362be2b5d11a0915b0"},"tools":{"phpmd":{"version":"2.13.0","url":"https://github.com/phpmd/phpmd/releases/download/2.13.0/phpmd.phar","requirements":{"php":{"php":">=5.3.9","ext-xml":"*"}},"checksum":null,"signature":"https://github.com/phpmd/phpmd/releases/download/2.13.0/phpmd.phar.asc"}},"composerLock":null},"phpcpd":{"api-version":"1.0.0","version":"1.1.1.0","type":"php-file","url":"https://phpcq.github.io/repository/phpcpd-1.1.1.0.php","signature":null,"requirements":{"php":{"php":"^7.3 || ^8.0","ext-dom":"*"},"tool":{"phpcpd":"^6.0"}},"checksum":{"type":"sha-512","value":"1189ce0bf3fade4cb4241f1d96f915ef8fc7651f4450dc79fdf464ee3d6be3009316f0d423ce2d4af9d76ad50807b7fdf4d77bfa6d9ee2c91d6eda32ea214433"},"tools":{"phpcpd":{"version":"6.0.3","url":"https://phar.phpunit.de/phpcpd-6.0.3.phar","requirements":{"php":{"php":">=7.3","ext-dom":"*"}},"checksum":{"type":"sha-256","value":"2cbaea7cfda1bb4299d863eb075e977c3f49055dd16d88529fae5150d48a84cb"},"signature":"https://phar.phpunit.de/phpcpd-6.0.3.phar.asc"}},"composerLock":null},"phploc":{"api-version":"1.0.0","version":"1.0.0.0","type":"php-file","url":"https://phpcq.github.io/repository/phploc-1.0.0.0.php","signature":null,"requirements":{"php":{"php":"^7.3 || ^8.0","ext-dom":"*","ext-json":"*"},"tool":{"phploc":"^3.0 || ^4.0 || ^5.0 || ^6.0 || ^7.0"}},"checksum":{"type":"sha-512","value":"f67b02d494796adf553cb3dd13ec06c1cb8e53c799954061749424251379541637538199afb3afa3c7a01cabd1cb6f1c53eb621f015dff9644c6c7cbf10c56d1"},"tools":{"phploc":{"version":"7.0.2","url":"https://phar.phpunit.de/phploc-7.0.2.phar","requirements":{"php":{"php":">=7.3","ext-dom":"*","ext-json":"*"}},"checksum":{"type":"sha-256","value":"3d59778ec86faf25fd00e3a329b2f9ad4a3c751ca91601ea7dab70f887b0bf46"},"signature":"https://phar.phpunit.de/phploc-7.0.2.phar.asc"}},"composerLock":null},"phpcs":{"api-version":"1.0.0","version":"1.1.0.0","type":"php-file","url":"https://phpcq.github.io/repository/phpcs-1.1.0.0.php","signature":null,"requirements":{"php":{"php":"^7.3 || ^8.0","ext-dom":"*"},"tool":{"phpcs":"^3.0 || ^2.0","phpcbf":"^3.0 || ^2.0"}},"checksum":{"type":"sha-512","value":"2737022369da1318cc4e0ea194e8a81019f7b079080d869aab878b7486052fdbe68fee3f28131f35573226def1aabd4bd005e038ee7b767c137b1107c1492a83"},"tools":{"phpcs":{"version":"3.7.1","url":"https://github.com/squizlabs/PHP_CodeSniffer/releases/download/3.7.1/phpcs.phar","requirements":{"php":{"php":">=5.4.0","ext-tokenizer":"*","ext-xmlwriter":"*","ext-simplexml":"*"}},"checksum":null,"signature":"https://github.com/squizlabs/PHP_CodeSniffer/releases/download/3.7.1/phpcs.phar.asc"},"phpcbf":{"version":"3.7.1","url":"https://github.com/squizlabs/PHP_CodeSniffer/releases/download/3.7.1/phpcbf.phar","requirements":{"php":{"php":">=5.4.0","ext-tokenizer":"*","ext-xmlwriter":"*","ext-simplexml":"*"}},"checksum":null,"signature":"https://github.com/squizlabs/PHP_CodeSniffer/releases/download/3.7.1/phpcbf.phar.asc"}},"composerLock":null},"composer-normalize":{"api-version":"1.0.0","version":"1.1.0.0","type":"php-file","url":"https://phpcq.github.io/repository/composer-normalize-1.1.0.0.php","signature":null,"requirements":{"php":{"php":"^7.3 || ^8.0","ext-json":"*"},"tool":{"composer-normalize":"^2.1"}},"checksum":{"type":"sha-512","value":"d59d3557cb20630734878a9115df5dd32d5aff815e5b15be36f6fb5d6e9d83dd36efd84215ab6529edcc924f600946f739a0d9e67723deff95c88346ab502498"},"tools":{"composer-normalize":{"version":"2.28.3","url":"https://github.com/ergebnis/composer-normalize/releases/download/2.28.3/composer-normalize.phar","requirements":{"php":{"php":"^7.4 || ^8.0"}},"checksum":null,"signature":"https://github.com/ergebnis/composer-normalize/releases/download/2.28.3/composer-normalize.phar.asc"}},"composerLock":null}},"tools":[]} \ No newline at end of file diff --git a/.phpcq.yaml.dist b/.phpcq.yaml.dist index 9fa7effa..ed969b08 100644 --- a/.phpcq.yaml.dist +++ b/.phpcq.yaml.dist @@ -75,7 +75,6 @@ tasks: - phpunit default: - verify - - composer-normalize - analyze phpmd: @@ -104,4 +103,4 @@ tasks: phpcbf: plugin: phpcs config: - <<: *phpcs-config \ No newline at end of file + <<: *phpcs-config From bde413e717977c3b220ee940a2d8abf6104634d2 Mon Sep 17 00:00:00 2001 From: Christian Schiffler Date: Mon, 31 Oct 2022 18:01:14 +0100 Subject: [PATCH 11/53] Fix phpcs issues (Move from PSR-2 to PSR-12) --- ...ExtendedLegacyDcaDataDefinitionBuilder.php | 3 +- .../Legacy/LegacyDcaDataDefinitionBuilder.php | 31 +- .../Dca/Palette/LegacyPalettesParser.php | 3 +- .../Dca/Populator/DataProviderPopulator.php | 2 +- src/Contao/Event/Subscriber.php | 34 +- src/Contao/Picker/PagePickerProvider.php | 2 +- src/Contao/SessionStorage.php | 4 +- .../DynamicParentTableSubscriber.php | 3 +- .../AbstractListShowAllHandler.php | 6 +- .../ActionHandler/CreateHandler.php | 10 +- .../ActionHandler/EditHandler.php | 7 +- .../ActionHandler/ListViewShowAllHandler.php | 3 +- .../MultipleHandler/EditAllHandler.php | 23 +- .../MultipleHandler/OverrideAllHandler.php | 16 +- .../MultipleHandler/PasteAllHandler.php | 8 +- .../MultipleHandler/SelectModelAllHandler.php | 3 +- .../SelectPropertyAllHandler.php | 13 +- .../ParentedListViewShowAllHandler.php | 18 +- .../ActionHandler/PasteHandler.php | 10 +- .../ActionHandler/SelectHandler.php | 9 +- .../ActionHandler/ToggleHandler.php | 7 +- .../View/Contao2BackendView/BaseView.php | 8 +- .../Contao2BackendView/ButtonRenderer.php | 3 +- .../ContaoWidgetManager.php | 12 +- .../Controller/ClipboardController.php | 8 +- .../View/Contao2BackendView/EditMask.php | 38 +- .../EventListener/BackButtonListener.php | 11 +- .../CreateModelButtonListener.php | 3 +- .../View/Contao2BackendView/FileSelect.php | 3 +- .../Filter/LanguageFilter.php | 3 +- .../Subscriber/CheckPermission.php | 7 +- .../Subscriber/GetGroupHeaderSubscriber.php | 590 +++++++++--------- .../Subscriber/MultipleHandlerSubscriber.php | 18 +- .../Subscriber/RichTextFileUuidSubscriber.php | 8 +- .../Subscriber/WidgetBuilder.php | 18 +- .../View/Contao2BackendView/TreePicker.php | 7 +- .../View/Contao2BackendView/TreeView.php | 7 +- .../View/Contao2BackendView/ViewHelpers.php | 3 +- .../Contao2BackendView/Widget/FileTree.php | 6 +- src/ContaoManager/Plugin.php | 4 +- src/Controller/Ajax.php | 56 +- src/Controller/Ajax3X.php | 8 +- src/Controller/BackendTreeController.php | 6 +- src/Controller/DefaultController.php | 25 +- src/Controller/ModelCollector.php | 3 +- src/Controller/TreeCollector.php | 13 +- src/DC/General.php | 6 +- src/Data/DefaultCollection.php | 9 +- src/Data/DefaultDataProvider.php | 7 +- src/Data/DefaultEditInformation.php | 3 +- src/Data/ModelInterface.php | 26 +- src/Data/ModelManipulator.php | 3 +- src/Data/NoOpDataProvider.php | 3 +- src/Data/PropertyValueBag.php | 1 - ...stractEventDrivenDataDefinitionBuilder.php | 4 +- .../ConditionChainInterface.php | 4 +- .../Definition/BasicDefinitionInterface.php | 8 +- .../DataProviderDefinitionInterface.php | 2 +- .../ModelRelationshipDefinitionInterface.php | 2 +- .../PalettesDefinitionInterface.php | 2 +- .../PropertiesDefinitionInterface.php | 2 +- .../GroupAndSortingInformationInterface.php | 22 +- .../Builder/Event/AddConditionEvent.php | 5 +- .../Builder/Event/CreateConditionEvent.php | 5 +- .../CreateDefaultPaletteConditionEvent.php | 2 +- .../Builder/Event/CreateLegendEvent.php | 2 +- .../Event/CreatePaletteCollectionEvent.php | 2 +- .../CreatePaletteConditionChainEvent.php | 2 +- .../Builder/Event/CreatePaletteEvent.php | 2 +- .../CreatePropertyConditionChainEvent.php | 2 +- .../Builder/Event/CreatePropertyEvent.php | 2 +- .../CreatePropertyValueConditionEvent.php | 5 +- .../Builder/Event/FinishConditionEvent.php | 3 +- .../Builder/Event/UsePropertyEvent.php | 2 +- .../Palette/Builder/PaletteBuilder.php | 3 +- .../Property/DumpingPropertyCondition.php | 1 - src/Panel/AbstractElement.php | 1 - ...AbstractPropertyOverrideEditAllHandler.php | 12 +- .../AbstractPropertyVisibilityHandler.php | 18 +- tests/CcaDcGeneralBundleTest.php | 1 - tests/Clipboard/ItemTest.php | 2 +- tests/Clipboard/UnsavedItemTest.php | 2 +- .../AbstractContainerCallbackListenerTest.php | 3 +- ...tReturningPropertyCallbackListenerTest.php | 2 +- .../LegacyDcaDataDefinitionBuilderTest.php | 20 +- .../Subscriber/CheckPermissionTest.php | 4 +- tests/Controller/ModelCollectorTest.php | 8 +- .../ParentChildConditionTest.php | 18 +- .../ModelRelationship/RootConditionTest.php | 6 +- tests/DcGeneralTest.php | 9 +- tests/Fixtures/Contao/Config.php | 7 +- tests/Fixtures/Contao/Template.php | 2 +- 92 files changed, 719 insertions(+), 621 deletions(-) diff --git a/src/Contao/Dca/Builder/Legacy/ExtendedLegacyDcaDataDefinitionBuilder.php b/src/Contao/Dca/Builder/Legacy/ExtendedLegacyDcaDataDefinitionBuilder.php index fde58edd..77e160f4 100644 --- a/src/Contao/Dca/Builder/Legacy/ExtendedLegacyDcaDataDefinitionBuilder.php +++ b/src/Contao/Dca/Builder/Legacy/ExtendedLegacyDcaDataDefinitionBuilder.php @@ -576,7 +576,8 @@ protected function parseListLabel(ListingConfigInterface $listing) */ protected function parseDynamicParentTableProperty(ContainerInterface $container) { - if ((null === ($propertyName = $this->getFromDca('dca_config/parent_table_property'))) + if ( + (null === ($propertyName = $this->getFromDca('dca_config/parent_table_property'))) || (null === ($sourceProvider = $this->getFromDca('config/ptable'))) || (null === ($dynamicParentTable = $this->getFromDca('config/dynamicPtable'))) ) { diff --git a/src/Contao/Dca/Builder/Legacy/LegacyDcaDataDefinitionBuilder.php b/src/Contao/Dca/Builder/Legacy/LegacyDcaDataDefinitionBuilder.php index 9100c59a..c118bd3d 100644 --- a/src/Contao/Dca/Builder/Legacy/LegacyDcaDataDefinitionBuilder.php +++ b/src/Contao/Dca/Builder/Legacy/LegacyDcaDataDefinitionBuilder.php @@ -204,7 +204,8 @@ protected function parsePropertyCallbacks(ContainerInterface $container, EventDi { foreach ((array) $this->getFromDca('fields') as $propName => $propInfo) { $args = [$container->getName(), $propName]; - foreach ([ + foreach ( + [ 'load_callback' => [ 'event' => DecodePropertyValueForWidgetEvent::NAME, 'class' => PropertyOnLoadCallbackListener::class @@ -229,7 +230,8 @@ protected function parsePropertyCallbacks(ContainerInterface $container, EventDi 'event' => ManipulateWidgetEvent::NAME, 'class' => PropertyInputFieldGetXLabelCallbackListener::class ] - ] as $name => $callback) { + ] as $name => $callback + ) { if (isset($propInfo[$name])) { $this->parseCallback($dispatcher, $propInfo[$name], $callback['event'], $args, $callback['class']); } @@ -251,7 +253,8 @@ protected function parsePropertyCallbacks(ContainerInterface $container, EventDi protected function parseCallbacks(ContainerInterface $container, EventDispatcherInterface $dispatcher) { $args = [$container->getName()]; - foreach ([ + foreach ( + [ 'config/onload_callback' => [ 'event' => CreateDcGeneralEvent::NAME, 'class' => ContainerOnLoadCallbackListener::class @@ -307,7 +310,8 @@ protected function parseCallbacks(ContainerInterface $container, EventDispatcher 'event' => GetBreadcrumbEvent::NAME, 'class' => ContainerGetBreadcrumbCallbackListener::class ] - ] as $name => $callback) { + ] as $name => $callback + ) { if ($callbacks = $this->getFromDca($name)) { if (isset($callback['event'])) { $this->parseCallback($dispatcher, $callbacks, $callback['event'], $args, $callback['class']); @@ -453,7 +457,8 @@ protected function parseBasicDefinition(ContainerInterface $container) $this->parseBasicMode($config); $this->parseBasicFlags($config); - if ((null !== ($filters = $this->getFromDca('list/sorting/filter'))) + if ( + (null !== ($filters = $this->getFromDca('list/sorting/filter'))) && \is_array($filters) && !empty($filters) ) { @@ -491,7 +496,8 @@ protected function parseDataProvider(ContainerInterface $container) } // If mode is 5, we need to define tree view. - if ((5 === $this->getFromDca('list/sorting/mode')) + if ( + (5 === $this->getFromDca('list/sorting/mode')) && !$container->getBasicDefinition()->getRootDataProvider() ) { $container->getBasicDefinition()->setRootDataProvider($container->getName()); @@ -1234,7 +1240,8 @@ private function parseOderPropertyInPalette(ContainerInterface $container) { foreach ($container->getPropertiesDefinition()->getProperties() as $property) { $extra = $property->getExtra(); - if (!isset($extra['orderField']) + if ( + !isset($extra['orderField']) || !$container->getPropertiesDefinition()->hasProperty($extra['orderField']) ) { continue; @@ -1247,7 +1254,8 @@ private function parseOderPropertyInPalette(ContainerInterface $container) foreach ($container->getPalettesDefinition()->getPalettes() as $palette) { foreach ($palette->getLegends() as $legend) { - if ((false === $legend->hasProperty($property->getName())) + if ( + (false === $legend->hasProperty($property->getName())) || (true === $legend->hasProperty($orderProperty->getName())) ) { continue; @@ -1381,9 +1389,7 @@ protected function parseSingleProperty(PropertyInterface $property, array $propI */ private function parseWidgetPageTree(PropertyInterface $property, array $propInfo) { - if (isset($propInfo['sourceName']) - || ('pageTree' !== $property->getWidgetType()) - ) { + if (isset($propInfo['sourceName']) || ('pageTree' !== $property->getWidgetType())) { return; } @@ -1458,7 +1464,8 @@ protected function parseProperties(ContainerInterface $container) $this->parseSingleProperty($property, $propInfo); $extra = $property->getExtra(); - if (isset($extra['orderField']) + if ( + isset($extra['orderField']) && \array_key_exists($extra['orderField'], (array) $this->getFromDca('fields')) ) { if (!$definition->hasProperty($extra['orderField'])) { diff --git a/src/Contao/Dca/Palette/LegacyPalettesParser.php b/src/Contao/Dca/Palette/LegacyPalettesParser.php index 8e8d2257..8046b651 100644 --- a/src/Contao/Dca/Palette/LegacyPalettesParser.php +++ b/src/Contao/Dca/Palette/LegacyPalettesParser.php @@ -294,7 +294,8 @@ public function parseSubpalettes(array $subpalettes, array $selectorFieldNames = $selectorProperty = []; // For selectable sub selector. - if (isset($properties[$selectorFieldName]) + if ( + isset($properties[$selectorFieldName]) && (0 < \substr_count($subPaletteSelector, '_')) ) { $selectorProperty = $properties[$selectorFieldName]; diff --git a/src/Contao/Dca/Populator/DataProviderPopulator.php b/src/Contao/Dca/Populator/DataProviderPopulator.php index ba812afd..a0b3200a 100644 --- a/src/Contao/Dca/Populator/DataProviderPopulator.php +++ b/src/Contao/Dca/Populator/DataProviderPopulator.php @@ -36,7 +36,7 @@ */ class DataProviderPopulator extends AbstractEventDrivenEnvironmentPopulator { - const PRIORITY = 100; + public const PRIORITY = 100; /** * The cached instances of the data provider. diff --git a/src/Contao/Event/Subscriber.php b/src/Contao/Event/Subscriber.php index 7afa18ee..07822419 100644 --- a/src/Contao/Event/Subscriber.php +++ b/src/Contao/Event/Subscriber.php @@ -293,10 +293,12 @@ public function initializePanels(ActionEvent $event) return; } - if (!\in_array( - $event->getAction()->getName(), - ['copy', 'create', 'paste', 'delete', 'move', 'undo', 'edit', 'toggle', 'showAll', 'show'] - ) + if ( + !\in_array( + $event->getAction()->getName(), + ['copy', 'create', 'paste', 'delete', 'move', 'undo', 'edit', 'toggle', 'showAll', 'show'], + true + ) ) { return; } @@ -305,7 +307,8 @@ public function initializePanels(ActionEvent $event) $definition = $environment->getDataDefinition(); $view = $environment->getView(); - if (!$view instanceof BaseView + if ( + !$view instanceof BaseView || !$view->getPanel() || !$definition->hasDefinition(Contao2BackendViewDefinitionInterface::NAME) ) { @@ -403,9 +406,11 @@ private static function renderArrayReadable(RenderReadablePropertyValueEvent $ev */ private static function renderTimestampReadable(RenderReadablePropertyValueEvent $event, $extra, $value) { - if (!isset($extra['rgxp']) + if ( + !isset($extra['rgxp']) || !(('date' === $extra['rgxp']) || ('time' === $extra['rgxp']) || ('datim' === $extra['rgxp'])) - || (null !== $event->getRendered())) { + || (null !== $event->getRendered()) + ) { return; } @@ -449,7 +454,8 @@ private static function renderDateTimePropertyIsTstamp(RenderReadablePropertyVal */ private static function renderSimpleCheckbox(RenderReadablePropertyValueEvent $event, $property, $extra, $value) { - if ((null !== $event->getRendered()) + if ( + (null !== $event->getRendered()) || !(!($extra['multiple'] ?? false) && ('checkbox' === $property->getWidgetType())) ) { return; @@ -492,9 +498,11 @@ private static function renderDateTimeValueInstance($event, $value) */ private static function renderReferenceReadable(RenderReadablePropertyValueEvent $event, $extra, $value) { - if (!isset($extra['reference']) + if ( + !isset($extra['reference']) || !\array_key_exists($value, (array) $extra['reference']) - || (null !== $event->getRendered())) { + || (null !== $event->getRendered()) + ) { return; } @@ -519,9 +527,11 @@ private static function renderReferenceReadable(RenderReadablePropertyValueEvent */ private static function renderTextAreaReadable(RenderReadablePropertyValueEvent $event, $property, $extra, $value) { - if ((empty($extra['allowHtml']) && empty($extra['preserveTags'])) + if ( + (empty($extra['allowHtml']) && empty($extra['preserveTags'])) || (null !== $event->getRendered()) - || ('textarea' !== $property->getWidgetType())) { + || ('textarea' !== $property->getWidgetType()) + ) { return; } diff --git a/src/Contao/Picker/PagePickerProvider.php b/src/Contao/Picker/PagePickerProvider.php index d9fabd58..bdec67a6 100644 --- a/src/Contao/Picker/PagePickerProvider.php +++ b/src/Contao/Picker/PagePickerProvider.php @@ -112,7 +112,7 @@ public function convertDcaValue(PickerConfig $config, $value) return (int) $value; } - return '{{link_url::'.$value.'}}'; + return '{{link_url::' . $value . '}}'; } /** diff --git a/src/Contao/SessionStorage.php b/src/Contao/SessionStorage.php index 513e1e24..af5db78c 100644 --- a/src/Contao/SessionStorage.php +++ b/src/Contao/SessionStorage.php @@ -75,9 +75,7 @@ public function __construct( foreach ($databaseKeys as $index => $databaseKeyItems) { foreach ((array) $databaseKeyItems as $databaseKey) { - if (('common' === $index) - || (0 === \strpos($index, 'DC_GENERAL_')) - ) { + if (('common' === $index) || (0 === \strpos($index, 'DC_GENERAL_'))) { $this->databaseKeys[$index][] = $databaseKey; continue; diff --git a/src/Contao/Subscriber/DynamicParentTableSubscriber.php b/src/Contao/Subscriber/DynamicParentTableSubscriber.php index dfc648c7..017a3ab4 100644 --- a/src/Contao/Subscriber/DynamicParentTableSubscriber.php +++ b/src/Contao/Subscriber/DynamicParentTableSubscriber.php @@ -65,7 +65,8 @@ public function handlePrePersistModelEvent(PrePersistModelEvent $event) $enviroment = $event->getEnvironment(); $dataDefinition = $enviroment->getDataDefinition(); - if (null === ($parentDataDefinition = $enviroment->getParentDataDefinition()) + if ( + null === ($parentDataDefinition = $enviroment->getParentDataDefinition()) || (false === $dataDefinition->getPropertiesDefinition()->hasProperty('ptable')) || (false === $dataDefinition->getBasicDefinition()->isDynamicParentTable()) ) { diff --git a/src/Contao/View/Contao2BackendView/ActionHandler/AbstractListShowAllHandler.php b/src/Contao/View/Contao2BackendView/ActionHandler/AbstractListShowAllHandler.php index 80428ef6..67425720 100644 --- a/src/Contao/View/Contao2BackendView/ActionHandler/AbstractListShowAllHandler.php +++ b/src/Contao/View/Contao2BackendView/ActionHandler/AbstractListShowAllHandler.php @@ -121,7 +121,8 @@ public function handleEvent(ActionEvent $event) $basic = $environment->getDataDefinition()->getBasicDefinition(); $action = $event->getAction(); - if (null !== $event->getResponse() + if ( + null !== $event->getResponse() || ('showAll' !== $action->getName()) || !$this->wantToHandle($basic->getMode(), $action) ) { @@ -322,7 +323,8 @@ protected function renderTemplate(ContaoBackendViewTemplate $template, Environme ->set('selectCheckBoxIdPrefix', 'models_') ->set('selectContainer', $this->getSelectContainer($environment)); - if ((null !== $template->get('action')) + if ( + (null !== $template->get('action')) && (false !== \strpos($template->get('action'), 'select=models')) ) { $template->set('action', \str_replace('select=models', 'select=properties', $template->get('action'))); diff --git a/src/Contao/View/Contao2BackendView/ActionHandler/CreateHandler.php b/src/Contao/View/Contao2BackendView/ActionHandler/CreateHandler.php index a22f67b7..a7317ca2 100644 --- a/src/Contao/View/Contao2BackendView/ActionHandler/CreateHandler.php +++ b/src/Contao/View/Contao2BackendView/ActionHandler/CreateHandler.php @@ -85,11 +85,11 @@ public function handleEvent(ActionEvent $event) // Only handle if we do not have a manual sorting or we know where to insert. // Manual sorting is handled by clipboard. - if (('create' !== $event->getAction()->getName()) - || (ViewHelpers::getManualSortingProperty($environment) - && !$inputProvider->hasParameter('after') - && !$inputProvider->hasParameter('into') - )) { + if ( + ViewHelpers::getManualSortingProperty($environment) + && !$inputProvider->hasParameter('after') + && !$inputProvider->hasParameter('into') + ) { return; } diff --git a/src/Contao/View/Contao2BackendView/ActionHandler/EditHandler.php b/src/Contao/View/Contao2BackendView/ActionHandler/EditHandler.php index 7b51ca93..124d4889 100644 --- a/src/Contao/View/Contao2BackendView/ActionHandler/EditHandler.php +++ b/src/Contao/View/Contao2BackendView/ActionHandler/EditHandler.php @@ -183,9 +183,10 @@ private function checkRestoreVersion(EnvironmentInterface $environment, ModelId $dataProviderDefinition = $environment->getDataDefinition()->getDataProviderDefinition(); $dataProvider = $environment->getDataProvider($modelId->getDataProviderName()); - if (!((null !== ($modelVersion = $inputProvider->getValue('version'))) - && ('tl_version' === $inputProvider->getValue('FORM_SUBMIT')) - && $dataProviderDefinition->getInformation($modelId->getDataProviderName())->isVersioningEnabled()) + if ( + !((null !== ($modelVersion = $inputProvider->getValue('version'))) + && ('tl_version' === $inputProvider->getValue('FORM_SUBMIT')) + && $dataProviderDefinition->getInformation($modelId->getDataProviderName())->isVersioningEnabled()) ) { return; } diff --git a/src/Contao/View/Contao2BackendView/ActionHandler/ListViewShowAllHandler.php b/src/Contao/View/Contao2BackendView/ActionHandler/ListViewShowAllHandler.php index c6fc89a5..147c8608 100644 --- a/src/Contao/View/Contao2BackendView/ActionHandler/ListViewShowAllHandler.php +++ b/src/Contao/View/Contao2BackendView/ActionHandler/ListViewShowAllHandler.php @@ -46,7 +46,8 @@ protected function wantToHandle($mode, Action $action) */ protected function determineTemplate($groupingInformation) { - if (isset($groupingInformation['mode']) + if ( + isset($groupingInformation['mode']) && (GroupAndSortingInformationInterface::GROUP_NONE !== $groupingInformation['mode']) ) { return $this->getTemplate('dcbe_general_grouping'); diff --git a/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/EditAllHandler.php b/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/EditAllHandler.php index 94991131..b7c5f125 100644 --- a/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/EditAllHandler.php +++ b/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/EditAllHandler.php @@ -62,8 +62,10 @@ public function __construct(RequestScopeDeterminator $scopeDeterminator) */ public function handleEvent(ActionEvent $event) { - if (!$this->scopeDeterminator->currentScopeIsBackend() - || ('editAll' !== $event->getAction()->getName())) { + if ( + !$this->scopeDeterminator->currentScopeIsBackend() + || ('editAll' !== $event->getAction()->getName()) + ) { return; } @@ -217,12 +219,14 @@ private function renderEditFields( $fields = []; foreach ($selectProperties as $selectProperty) { - if (!$this->ensurePropertyVisibleInModel( - $action, - $selectProperty->getName(), - $visibleModel, - $environment - )) { + if ( + !$this->ensurePropertyVisibleInModel( + $action, + $selectProperty->getName(), + $visibleModel, + $environment + ) + ) { $fields[] = $this->injectSelectParentPropertyInformation($action, $selectProperty, $editModel, $environment); @@ -342,7 +346,8 @@ private function markEditErrors( PropertyInterface $selectProperty, PropertyValueBagInterface $propertyValuesBag ) { - if (($editErrors = $propertyValuesBag->getInvalidPropertyErrors()) + if ( + ($editErrors = $propertyValuesBag->getInvalidPropertyErrors()) && \array_key_exists($selectProperty->getName(), $editErrors) ) { $propertyValuesBag->markPropertyValueAsInvalid( diff --git a/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/OverrideAllHandler.php b/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/OverrideAllHandler.php index 9e16edbe..87014428 100644 --- a/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/OverrideAllHandler.php +++ b/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/OverrideAllHandler.php @@ -58,8 +58,10 @@ public function __construct(RequestScopeDeterminator $scopeDeterminator) */ public function handleEvent(ActionEvent $event) { - if (!$this->scopeDeterminator->currentScopeIsBackend() - || ('overrideAll' !== $event->getAction()->getName())) { + if ( + !$this->scopeDeterminator->currentScopeIsBackend() + || ('overrideAll' !== $event->getAction()->getName()) + ) { return; } @@ -145,9 +147,7 @@ protected function handleInvalidPropertyValueBag( @\trigger_error('This function where remove in 3.0. ' . __CLASS__ . '::' . __FUNCTION__, E_USER_DEPRECATED); // @codingStandardsIgnoreEnd - if ((null === $propertyValueBag) - || (null === $model) - ) { + if ((null === $propertyValueBag) || (null === $model)) { return; } @@ -340,7 +340,8 @@ private function getModelFromWidget(Widget $widget) */ private function getPropertyValueErrors(PropertyValueBagInterface $propertyValueBag, $propertyName, array $errors) { - if ((null !== $propertyValueBag) + if ( + (null !== $propertyValueBag) && $propertyValueBag->hasPropertyValue($propertyName) && $propertyValueBag->isPropertyValueInvalid($propertyName) ) { @@ -378,7 +379,8 @@ private function setDefaultValue( return; } - if ($propertiesDefinition->hasProperty($propertyName) + if ( + $propertiesDefinition->hasProperty($propertyName) && !$environment->getInputProvider()->hasValue($propertyName) ) { $propertyValueBag->setPropertyValue( diff --git a/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/PasteAllHandler.php b/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/PasteAllHandler.php index 614ee0bd..493f936d 100644 --- a/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/PasteAllHandler.php +++ b/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/PasteAllHandler.php @@ -73,9 +73,7 @@ public function __construct(RequestScopeDeterminator $scopeDeterminator) */ public function handleEvent(ActionEvent $event) { - if (!$this->scopeDeterminator->currentScopeIsBackend() - || ('pasteAll' !== $event->getAction()->getName()) - ) { + if (!$this->scopeDeterminator->currentScopeIsBackend() || ('pasteAll' !== $event->getAction()->getName())) { return; } @@ -223,9 +221,7 @@ protected function getHierarchyCollection(array $clipboardItems, EnvironmentInte $previousItem = null; foreach ($clipboardItems as $clipboardItem) { $modelId = $clipboardItem->getModelId(); - if (!$modelId - || \array_key_exists($modelId->getSerialized(), $collection) - ) { + if (!$modelId || \array_key_exists($modelId->getSerialized(), $collection)) { continue; } diff --git a/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/SelectModelAllHandler.php b/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/SelectModelAllHandler.php index 5eede917..256a39d7 100644 --- a/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/SelectModelAllHandler.php +++ b/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/SelectModelAllHandler.php @@ -53,7 +53,8 @@ public function __construct(RequestScopeDeterminator $scopeDeterminator) */ public function handleEvent(ActionEvent $event) { - if (!$this->scopeDeterminator->currentScopeIsBackend() + if ( + !$this->scopeDeterminator->currentScopeIsBackend() || ('selectModelAll' !== $event->getAction()->getName()) ) { return; diff --git a/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/SelectPropertyAllHandler.php b/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/SelectPropertyAllHandler.php index e9a16f31..2f9a05d9 100644 --- a/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/SelectPropertyAllHandler.php +++ b/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/SelectPropertyAllHandler.php @@ -60,7 +60,8 @@ class SelectPropertyAllHandler extends AbstractListShowAllHandler */ public function handleEvent(ActionEvent $event) { - if (!$this->scopeDeterminator->currentScopeIsBackend() + if ( + !$this->scopeDeterminator->currentScopeIsBackend() || ('selectPropertyAll' !== $event->getAction()->getName()) ) { return null; @@ -199,16 +200,15 @@ private function getCollection(DataProviderInterface $dataProvider, EnvironmentI */ private function isPropertyAllowed(PropertyInterface $property, EnvironmentInterface $environment) { - if (!$property->getWidgetType() - || ('dummyProperty' === $property->getWidgetType()) - ) { + if (!$property->getWidgetType() || ('dummyProperty' === $property->getWidgetType())) { return false; } $translator = $environment->getTranslator(); $extra = (array) $property->getExtra(); - if ($this->isPropertyAllowedByEdit($extra, $environment) + if ( + $this->isPropertyAllowedByEdit($extra, $environment) || $this->isPropertyAllowedByOverride($extra, $environment) ) { Message::addInfo( @@ -364,7 +364,8 @@ protected function renderTemplate(ContaoBackendViewTemplate $template, Environme ->set('selectCheckBoxName', 'properties[]') ->set('selectCheckBoxIdPrefix', 'properties_'); - if ((null !== $template->get('action')) + if ( + (null !== $template->get('action')) && (false !== \strpos($template->get('action'), 'select=properties')) ) { $template->set('action', \str_replace('select=properties', 'select=edit', $template->get('action'))); diff --git a/src/Contao/View/Contao2BackendView/ActionHandler/ParentedListViewShowAllHandler.php b/src/Contao/View/Contao2BackendView/ActionHandler/ParentedListViewShowAllHandler.php index dcba34e5..6cfd4091 100644 --- a/src/Contao/View/Contao2BackendView/ActionHandler/ParentedListViewShowAllHandler.php +++ b/src/Contao/View/Contao2BackendView/ActionHandler/ParentedListViewShowAllHandler.php @@ -95,8 +95,10 @@ protected function renderModel(ModelInterface $model, EnvironmentInterface $envi protected function determineTemplate($groupingInformation) { // Add template. - if (isset($groupingInformation['mode']) - && (GroupAndSortingInformationInterface::GROUP_NONE !== $groupingInformation['mode'])) { + if ( + isset($groupingInformation['mode']) + && (GroupAndSortingInformationInterface::GROUP_NONE !== $groupingInformation['mode']) + ) { return $this->getTemplate('dcbe_general_grouping'); } return $this->getTemplate('dcbe_general_parentView'); @@ -257,9 +259,11 @@ private function renderForCheckbox(PropertyInterface $property, $value, &$isRend { $evaluation = $property->getExtra(); - if ((true === $isRendered) + if ( + (true === $isRendered) || (isset($evaluation['multiple']) && $evaluation['multiple']) - || !('checkbox' === $property->getWidgetType())) { + || !('checkbox' === $property->getWidgetType()) + ) { return $value; } @@ -288,10 +292,12 @@ private function renderForDateTime( ) { $evaluation = $property->getExtra(); - if ((true === $isRendered) + if ( + (true === $isRendered) || !$value || !isset($evaluation['rgxp']) - || !\in_array($evaluation['rgxp'], ['date', 'time', 'datim'])) { + || !\in_array($evaluation['rgxp'], ['date', 'time', 'datim']) + ) { return $value; } diff --git a/src/Contao/View/Contao2BackendView/ActionHandler/PasteHandler.php b/src/Contao/View/Contao2BackendView/ActionHandler/PasteHandler.php index 4d29253b..78162aa1 100644 --- a/src/Contao/View/Contao2BackendView/ActionHandler/PasteHandler.php +++ b/src/Contao/View/Contao2BackendView/ActionHandler/PasteHandler.php @@ -98,10 +98,12 @@ protected function process(EnvironmentInterface $environment) } // Check if it is a simple create-paste of a single model, if so, redirect to edit view. - if ($this->isSimpleCreatePaste( - $clipboard, - $environment->getDataDefinition()->getBasicDefinition()->getDataProvider() - )) { + if ( + $this->isSimpleCreatePaste( + $clipboard, + $environment->getDataDefinition()->getBasicDefinition()->getDataProvider() + ) + ) { return $this->callAction($environment, 'create'); } diff --git a/src/Contao/View/Contao2BackendView/ActionHandler/SelectHandler.php b/src/Contao/View/Contao2BackendView/ActionHandler/SelectHandler.php index 3dc3fe4c..3b31d54d 100644 --- a/src/Contao/View/Contao2BackendView/ActionHandler/SelectHandler.php +++ b/src/Contao/View/Contao2BackendView/ActionHandler/SelectHandler.php @@ -126,7 +126,8 @@ private function process(Action $action, EnvironmentInterface $environment) */ private function getSubmitAction(EnvironmentInterface $environment, $regardSelectMode = false) { - if (!$regardSelectMode + if ( + !$regardSelectMode && $environment->getInputProvider()->hasParameter('select') && !$environment->getInputProvider()->hasValue('properties') ) { @@ -155,7 +156,8 @@ private function getSubmitAction(EnvironmentInterface $environment, $regardSelec private function determineAction(EnvironmentInterface $environment) { foreach (['delete', 'cut', 'copy', 'override', 'edit'] as $action) { - if ($environment->getInputProvider()->hasValue($action) + if ( + $environment->getInputProvider()->hasValue($action) || $environment->getInputProvider()->hasValue($action . '_save') || $environment->getInputProvider()->hasValue($action . '_saveNback') ) { @@ -192,7 +194,8 @@ function ($value) use ($inputProvider, &$regardSelectMode) { ['edit_save', 'edit_saveNback', 'override_save', 'override_saveNback', 'delete', 'copy', 'cut'] ); - if (('auto' === $inputProvider->getValue('SUBMIT_TYPE')) + if ( + ('auto' === $inputProvider->getValue('SUBMIT_TYPE')) && ('edit' === $inputProvider->getParameter('select')) ) { return true; diff --git a/src/Contao/View/Contao2BackendView/ActionHandler/ToggleHandler.php b/src/Contao/View/Contao2BackendView/ActionHandler/ToggleHandler.php index d688a3fc..508b8925 100644 --- a/src/Contao/View/Contao2BackendView/ActionHandler/ToggleHandler.php +++ b/src/Contao/View/Contao2BackendView/ActionHandler/ToggleHandler.php @@ -113,7 +113,8 @@ protected function process( $newState = $this->determineNewState($environment->getInputProvider(), $operation->isInverse()); // Override the language for language aware toggling. - if (($operation instanceof TranslatedToggleCommandInterface) + if ( + ($operation instanceof TranslatedToggleCommandInterface) && ($dataProvider instanceof MultiLanguageDataProviderInterface) ) { $language = $dataProvider->getCurrentLanguage(); @@ -189,9 +190,7 @@ private function getModelId(EnvironmentInterface $environment) $modelId = ModelId::fromSerialized($inputProvider->getParameter('id')); } - if (!(isset($modelId) - && ($environment->getDataDefinition()->getName() === $modelId->getDataProviderName())) - ) { + if (!(isset($modelId) && ($environment->getDataDefinition()->getName() === $modelId->getDataProviderName()))) { return null; } diff --git a/src/Contao/View/Contao2BackendView/BaseView.php b/src/Contao/View/Contao2BackendView/BaseView.php index 54fbe2bd..919cb76e 100644 --- a/src/Contao/View/Contao2BackendView/BaseView.php +++ b/src/Contao/View/Contao2BackendView/BaseView.php @@ -118,9 +118,10 @@ public function handleAction(ActionEvent $event) { $GLOBALS['TL_CSS']['cca.dc-general.generalDriver'] = 'bundles/ccadcgeneral/css/generalDriver.css'; - if ((null !== $event->getResponse()) + if ( + (null !== $event->getResponse()) || $event->getEnvironment()->getDataDefinition()->getName() - !== $this->environment->getDataDefinition()->getName() + !== $this->environment->getDataDefinition()->getName() ) { return; } @@ -533,7 +534,8 @@ private function addAjaxPropertyForEditAll() { $inputProvider = $this->getEnvironment()->getInputProvider(); - if (('select' !== $inputProvider->getParameter('act')) + if ( + ('select' !== $inputProvider->getParameter('act')) && ('edit' !== $inputProvider->getParameter('select')) && ('edit' !== $inputProvider->getParameter('mode')) ) { diff --git a/src/Contao/View/Contao2BackendView/ButtonRenderer.php b/src/Contao/View/Contao2BackendView/ButtonRenderer.php index 8a4d530c..7387743e 100644 --- a/src/Contao/View/Contao2BackendView/ButtonRenderer.php +++ b/src/Contao/View/Contao2BackendView/ButtonRenderer.php @@ -595,7 +595,8 @@ private function isTogglerInActiveState($command, $model) $dataProvider = $this->environment->getDataProvider($model->getProviderName()); $propModel = $model; - if ($command instanceof TranslatedToggleCommandInterface + if ( + $command instanceof TranslatedToggleCommandInterface && $dataProvider instanceof MultiLanguageDataProviderInterface ) { $language = $dataProvider->getCurrentLanguage(); diff --git a/src/Contao/View/Contao2BackendView/ContaoWidgetManager.php b/src/Contao/View/Contao2BackendView/ContaoWidgetManager.php index afb69823..224a9fac 100644 --- a/src/Contao/View/Contao2BackendView/ContaoWidgetManager.php +++ b/src/Contao/View/Contao2BackendView/ContaoWidgetManager.php @@ -167,7 +167,8 @@ public function hasWidget($property) */ public function loadRichTextEditor($buffer, Widget $widget) { - if ((null === $widget->rte) + if ( + (null === $widget->rte) || ((0 !== (\strncmp($widget->rte, 'tiny', 4))) && (0 !== \strncmp($widget->rte, 'ace', 3))) ) { @@ -212,7 +213,8 @@ protected function getUniqueId($propertyName) $selector = 'ctrl_' . $propertyName; - if (('select' !== $inputProvider->getParameter('act')) + if ( + ('select' !== $inputProvider->getParameter('act')) || (false === $inputProvider->hasValue('edit') && false === $inputProvider->hasValue('edit_save')) ) { return $selector; @@ -331,7 +333,8 @@ protected function generateHelpText($property) $label = $propInfo->getDescription(); $widgetType = $propInfo->getWidgetType(); - if (empty($label) + if ( + empty($label) || ('password' === $widgetType) || !\is_string($label) || !$GLOBALS['TL_CONFIG']['showHelp'] @@ -526,7 +529,8 @@ protected function widgetAddError( PropertyValueBagInterface $inputValues = null, $ignoreErrors = false ) { - if (!(!$ignoreErrors && $inputValues && $inputValues->hasPropertyValue($property) + if ( + !(!$ignoreErrors && $inputValues && $inputValues->hasPropertyValue($property) && $inputValues->isPropertyValueInvalid($property)) ) { return; diff --git a/src/Contao/View/Contao2BackendView/Controller/ClipboardController.php b/src/Contao/View/Contao2BackendView/Controller/ClipboardController.php index d1d20cd6..f93d1ddd 100644 --- a/src/Contao/View/Contao2BackendView/Controller/ClipboardController.php +++ b/src/Contao/View/Contao2BackendView/Controller/ClipboardController.php @@ -102,7 +102,8 @@ public function handleAction(ActionEvent $event) return; } - if ('create' === $actionName + if ( + 'create' === $actionName || 'cut' === $actionName || 'copy' === $actionName || 'deepcopy' === $actionName @@ -125,14 +126,15 @@ private function checkPermission(ActionEvent $event) $environment = $event->getEnvironment(); $basicDefinition = $environment->getDataDefinition()->getBasicDefinition(); - if ((('create' === $actionName) && (true === $basicDefinition->isCreatable())) + if ( + (('create' === $actionName) && (true === $basicDefinition->isCreatable())) || (('cut' === $actionName) && (true === $basicDefinition->isEditable())) || (false === \in_array($actionName, ['create', 'cut'])) ) { return true; } - $permissionMessage = 'You have no permission for model ' . $actionName .' '; + $permissionMessage = 'You have no permission for model ' . $actionName . ' '; switch ($actionName) { case 'create': $permissionMessage .= 'in ' . $environment->getDataDefinition()->getName(); diff --git a/src/Contao/View/Contao2BackendView/EditMask.php b/src/Contao/View/Contao2BackendView/EditMask.php index 8f1ef520..48c3dc7d 100644 --- a/src/Contao/View/Contao2BackendView/EditMask.php +++ b/src/Contao/View/Contao2BackendView/EditMask.php @@ -392,8 +392,10 @@ protected function getEditButtons() $buttons['saveNclose'] = $buttonTemplate->parse(); } - if ($basicDefinition->isCreatable() - && !$this->getEnvironment()->getInputProvider()->getParameter('nc')) { + if ( + $basicDefinition->isCreatable() + && !$this->getEnvironment()->getInputProvider()->getParameter('nc') + ) { $buttonTemplate->setData( [ 'label' => $this->getButtonLabel('saveNcreate'), @@ -423,11 +425,13 @@ protected function getEditButtons() ] ); $buttons['saveNedit'] = $buttonTemplate->parse(); - } elseif (!$this->isPopup() - && ((BasicDefinitionInterface::MODE_PARENTEDLIST === $basicDefinition->getMode()) - || '' !== $basicDefinition->getParentDataProvider() - || $basicDefinition->isSwitchToEditEnabled() - ) + } elseif ( + !$this->isPopup() + && ( + (BasicDefinitionInterface::MODE_PARENTEDLIST === $basicDefinition->getMode()) + || '' !== $basicDefinition->getParentDataProvider() + || $basicDefinition->isSwitchToEditEnabled() + ) ) { $buttonTemplate->setData( [ @@ -508,7 +512,8 @@ protected function buildFieldSet($widgetManager, $palette, $propertyValues) $this->ensurePropertyExists($property->getName(), $propertyDefinitions); // If this property is invalid, fetch the error. - if ((!$isAutoSubmit) + if ( + (!$isAutoSubmit) && $propertyValues && $propertyValues->hasPropertyValue($property->getName()) && $propertyValues->isPropertyValueInvalid($property->getName()) @@ -578,7 +583,8 @@ protected function storeVersion(ModelInterface $model) // Compare version and current record. $currentVersion = $dataProvider->getActiveVersion($modelId); - if (!$currentVersion + if ( + !$currentVersion || !$dataProvider->sameModels($model, $dataProvider->getVersion($modelId, $currentVersion)) ) { $user = BackendUser::getInstance(); @@ -888,14 +894,16 @@ public function execute() */ private function executeMultiLanguage(ContaoBackendViewTemplate $template) { - if (\in_array( - MultiLanguageDataProviderInterface::class, - \class_implements( - $this->getEnvironment()->getDataProvider( - $this->model->getProviderName() + if ( + \in_array( + MultiLanguageDataProviderInterface::class, + \class_implements( + $this->getEnvironment()->getDataProvider( + $this->model->getProviderName() + ) ) ) - )) { + ) { /** @var MultiLanguageDataProviderInterface $dataProvider */ $dataProvider = $this->getEnvironment()->getDataProvider(); diff --git a/src/Contao/View/Contao2BackendView/EventListener/BackButtonListener.php b/src/Contao/View/Contao2BackendView/EventListener/BackButtonListener.php index eccfdbb0..42c83174 100644 --- a/src/Contao/View/Contao2BackendView/EventListener/BackButtonListener.php +++ b/src/Contao/View/Contao2BackendView/EventListener/BackButtonListener.php @@ -53,15 +53,18 @@ public function handle(GetGlobalButtonEvent $event) $environment = $event->getEnvironment(); $inputProvider = $environment->getInputProvider(); - if (!(\in_array($inputProvider->getParameter('act'), ['edit', 'create']) - || (null !== $inputProvider->getParameter('pid') - || (null !== $inputProvider->getParameter('select')))) + if ( + !( + \in_array($inputProvider->getParameter('act'), ['edit', 'create']) + || (null !== $inputProvider->getParameter('pid') || (null !== $inputProvider->getParameter('select'))) + ) ) { $event->setHtml(''); return; } - if (('select' === $inputProvider->getParameter('act')) + if ( + ('select' === $inputProvider->getParameter('act')) && ('models' !== $inputProvider->getParameter('select')) ) { return; diff --git a/src/Contao/View/Contao2BackendView/EventListener/CreateModelButtonListener.php b/src/Contao/View/Contao2BackendView/EventListener/CreateModelButtonListener.php index b0fb45e0..8af94452 100644 --- a/src/Contao/View/Contao2BackendView/EventListener/CreateModelButtonListener.php +++ b/src/Contao/View/Contao2BackendView/EventListener/CreateModelButtonListener.php @@ -60,7 +60,8 @@ public function handle(GetGlobalButtonEvent $event) return; } - if ((BasicDefinitionInterface::MODE_PARENTEDLIST === $mode) + if ( + (BasicDefinitionInterface::MODE_PARENTEDLIST === $mode) || (BasicDefinitionInterface::MODE_HIERARCHICAL === $mode) ) { $filter = new Filter(); diff --git a/src/Contao/View/Contao2BackendView/FileSelect.php b/src/Contao/View/Contao2BackendView/FileSelect.php index a1bd788e..4406a30f 100644 --- a/src/Contao/View/Contao2BackendView/FileSelect.php +++ b/src/Contao/View/Contao2BackendView/FileSelect.php @@ -134,7 +134,8 @@ public function run() $template->manager = $GLOBALS['TL_LANG']['MSC']['treepickerManager']; $template->managerHref = ''; - if ('tl_files' !== $inputProvider->getValue('do') + if ( + 'tl_files' !== $inputProvider->getValue('do') && (null === $GLOBALS['TL_DCA']['tl_files']['list']['sorting']['breadcrumb']) ) { Backend::addFilesBreadcrumb('tl_files_picker'); diff --git a/src/Contao/View/Contao2BackendView/Filter/LanguageFilter.php b/src/Contao/View/Contao2BackendView/Filter/LanguageFilter.php index bd017e6d..7e05d27c 100644 --- a/src/Contao/View/Contao2BackendView/Filter/LanguageFilter.php +++ b/src/Contao/View/Contao2BackendView/Filter/LanguageFilter.php @@ -156,7 +156,8 @@ private function checkLanguageSubmit($environment, $languages) } // Get/Check the new language. - if ($inputProvider->hasValue('language') + if ( + $inputProvider->hasValue('language') && \array_key_exists($inputProvider->getValue('language'), $languages) ) { $session['ml_support'][$environment->getDataDefinition()->getName()] = $inputProvider->getValue('language'); diff --git a/src/Contao/View/Contao2BackendView/Subscriber/CheckPermission.php b/src/Contao/View/Contao2BackendView/Subscriber/CheckPermission.php index 7bab59b9..57588b04 100644 --- a/src/Contao/View/Contao2BackendView/Subscriber/CheckPermission.php +++ b/src/Contao/View/Contao2BackendView/Subscriber/CheckPermission.php @@ -193,7 +193,8 @@ public function checkPermissionIsCreatable(BuildDataDefinitionEvent $event) */ private function getVisibilityConditionChain($property) { - if (($chain = $property->getVisibleCondition()) + if ( + ($chain = $property->getVisibleCondition()) && ($chain instanceof PropertyConditionChain) && $chain->getConjunction() === PropertyConditionChain::AND_CONJUNCTION ) { @@ -221,9 +222,7 @@ private function disableCommandByActionName(CommandCollectionInterface $commands $disableCommand = false; - if (\array_key_exists('act', $parameters) - && ($parameters['act'] === $actionName) - ) { + if (\array_key_exists('act', $parameters) && ($parameters['act'] === $actionName)) { $disableCommand = true; } diff --git a/src/Contao/View/Contao2BackendView/Subscriber/GetGroupHeaderSubscriber.php b/src/Contao/View/Contao2BackendView/Subscriber/GetGroupHeaderSubscriber.php index b7019e19..89c3b617 100644 --- a/src/Contao/View/Contao2BackendView/Subscriber/GetGroupHeaderSubscriber.php +++ b/src/Contao/View/Contao2BackendView/Subscriber/GetGroupHeaderSubscriber.php @@ -1,295 +1,295 @@ - - * @author Christian Schiffler - * @author Stefan Heimes - * @author Sven Baumann - * @author Ingolf Steinhardt - * @copyright 2013-2022 Contao Community Alliance. - * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later - * @filesource - */ - -namespace ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Subscriber; - -use Contao\Config; -use ContaoCommunityAlliance\Contao\Bindings\ContaoEvents; -use ContaoCommunityAlliance\Contao\Bindings\Events\Date\ParseDateEvent; -use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminatorAwareTrait; -use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\GetGroupHeaderEvent; -use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\ViewHelpers; -use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; -use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\Properties\PropertyInterface; -use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\GroupAndSortingInformationInterface; -use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; -use ContaoCommunityAlliance\Translator\TranslatorInterface; -use Symfony\Component\EventDispatcher\EventDispatcherInterface; - -/** - * Handles the group header formatting. - */ -class GetGroupHeaderSubscriber -{ - use RequestScopeDeterminatorAwareTrait; - - /** - * The event dispatcher. - * - * @var EventDispatcherInterface - */ - private $dispatcher; - - /** - * The translator. - * - * @var TranslatorInterface - */ - private $translator; - - /** - * Create a new instance. - * - * @param EventDispatcherInterface $dispatcher The event dispatcher. - * @param TranslatorInterface $translator The translator. - */ - public function __construct(EventDispatcherInterface $dispatcher, TranslatorInterface $translator) - { - $this->dispatcher = $dispatcher; - $this->translator = $translator; - } - - /** - * Handle the subscribed event. - * - * @param GetGroupHeaderEvent $event The event. - * - * @return void - */ - public function handle(GetGroupHeaderEvent $event) - { - if ((null !== $event->getValue()) || !$this->scopeDeterminator->currentScopeIsBackend()) { - return; - } - - $environment = $event->getEnvironment(); - $property = $environment - ->getDataDefinition() - ->getPropertiesDefinition() - ->getProperty($event->getGroupField()); - - // No property? Get out! - if (!$property) { - $event->setValue('-'); - return; - } - - $value = $this->formatGroupHeader( - $environment, - $event->getModel(), - $property, - $event->getGroupingMode(), - $event->getGroupingLength() - ); - - if (null !== $value) { - $event->setValue($value); - } - } - - /** - * Get the group header. - * - * @param EnvironmentInterface $environment The environment. - * @param ModelInterface $model The model. - * @param PropertyInterface $property The property. - * @param int $groupingMode The grouping mode. - * @param int $groupingLength The grouping length. - * - * @return string - */ - protected function formatGroupHeader( - EnvironmentInterface $environment, - ModelInterface $model, - PropertyInterface $property, - $groupingMode, - $groupingLength - ) { - $evaluation = $property->getExtra(); - - if (isset($evaluation['multiple']) && !$evaluation['multiple'] && ('checkbox' === $property->getWidgetType())) { - return $this->formatCheckboxOptionLabel($model->getProperty($property->getName())); - } - if (GroupAndSortingInformationInterface::GROUP_NONE !== $groupingMode) { - return $this->formatByGroupingMode($groupingMode, $groupingLength, $environment, $property, $model); - } - - $value = ViewHelpers::getReadableFieldValue($environment, $property, $model); - - if (isset($evaluation['reference'])) { - $remoteNew = $evaluation['reference'][$value]; - } elseif (\array_is_assoc($property->getOptions())) { - $options = $property->getOptions(); - $remoteNew = $options[$value]; - } else { - $remoteNew = $value; - } - - if (\is_array($remoteNew)) { - $remoteNew = $remoteNew[0]; - } - - if (empty($remoteNew)) { - $remoteNew = '-'; - } - - return $remoteNew; - } - - /** - * Format the grouping header for a checkbox option. - * - * @param string $value The given value. - * - * @return string - */ - private function formatCheckboxOptionLabel($value) - { - return ('' !== $value) - ? \ucfirst($this->translator->translate('MSC.yes')) - : \ucfirst($this->translator->translate('MSC.no')); - } - - /** - * Format the group header by the grouping mode. - * - * @param int $groupingMode The grouping mode. - * @param int $groupingLength The grouping length. - * @param EnvironmentInterface $environment The environment. - * @param PropertyInterface $property The current property definition. - * @param ModelInterface $model The current data model. - * - * @return string - */ - private function formatByGroupingMode( - $groupingMode, - $groupingLength, - EnvironmentInterface $environment, - PropertyInterface $property, - ModelInterface $model - ) { - switch ($groupingMode) { - case GroupAndSortingInformationInterface::GROUP_CHAR: - return $this->formatByCharGrouping( - ViewHelpers::getReadableFieldValue($environment, $property, $model), - $groupingLength - ); - - case GroupAndSortingInformationInterface::GROUP_DAY: - return $this->formatByDayGrouping((int) $model->getProperty($property->getName())); - - case GroupAndSortingInformationInterface::GROUP_MONTH: - return $this->formatByMonthGrouping((int) $model->getProperty($property->getName())); - - case GroupAndSortingInformationInterface::GROUP_YEAR: - return $this->formatByYearGrouping((int) $model->getProperty($property->getName())); - - default: - return ViewHelpers::getReadableFieldValue($environment, $property, $model); - } - } - - /** - * Format a value for char grouping. - * - * @param string $value The value. - * @param int $groupingLength The group length. - * - * @return string - */ - private function formatByCharGrouping($value, $groupingLength) - { - if ('' === $value) { - return '-'; - } - - return mb_strtoupper(mb_substr($value, 0, $groupingLength ?: null)); - } - - /** - * Render a grouping header for day. - * - * @param int $value The value. - * - * @return string - */ - private function formatByDayGrouping($value) - { - $value = $this->getTimestamp($value); - if ('' === $value) { - return '-'; - } - $event = new ParseDateEvent($value, Config::get('dateFormat')); - $this->dispatcher->dispatch($event, ContaoEvents::DATE_PARSE); - - return $event->getResult(); - } - - /** - * Render a grouping header for month. - * - * @param int $value The value. - * - * @return string - */ - private function formatByMonthGrouping($value) - { - $value = $this->getTimestamp($value); - if ('' === $value) { - return '-'; - } - $event = new ParseDateEvent($value, 'F Y'); - $this->dispatcher->dispatch($event, ContaoEvents::DATE_PARSE); - - return $event->getResult(); - } - - /** - * Render a grouping header for year. - * - * @param int $value The value. - * - * @return string - */ - private function formatByYearGrouping($value) - { - $value = $this->getTimestamp($value); - if ('' === $value) { - return '-'; - } - - return date('Y', $value); - } - - /** - * Make sure a timestamp is returned. - * - * @param int|\DateTime $value The given date. - * - * @return int - */ - private function getTimestamp($value) - { - return ($value instanceof \DateTime) ? $value->getTimestamp() : $value; - } -} + + * @author Christian Schiffler + * @author Stefan Heimes + * @author Sven Baumann + * @author Ingolf Steinhardt + * @copyright 2013-2022 Contao Community Alliance. + * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later + * @filesource + */ + +namespace ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Subscriber; + +use Contao\Config; +use ContaoCommunityAlliance\Contao\Bindings\ContaoEvents; +use ContaoCommunityAlliance\Contao\Bindings\Events\Date\ParseDateEvent; +use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminatorAwareTrait; +use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\GetGroupHeaderEvent; +use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\ViewHelpers; +use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\Properties\PropertyInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\GroupAndSortingInformationInterface; +use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; +use ContaoCommunityAlliance\Translator\TranslatorInterface; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; + +/** + * Handles the group header formatting. + */ +class GetGroupHeaderSubscriber +{ + use RequestScopeDeterminatorAwareTrait; + + /** + * The event dispatcher. + * + * @var EventDispatcherInterface + */ + private $dispatcher; + + /** + * The translator. + * + * @var TranslatorInterface + */ + private $translator; + + /** + * Create a new instance. + * + * @param EventDispatcherInterface $dispatcher The event dispatcher. + * @param TranslatorInterface $translator The translator. + */ + public function __construct(EventDispatcherInterface $dispatcher, TranslatorInterface $translator) + { + $this->dispatcher = $dispatcher; + $this->translator = $translator; + } + + /** + * Handle the subscribed event. + * + * @param GetGroupHeaderEvent $event The event. + * + * @return void + */ + public function handle(GetGroupHeaderEvent $event) + { + if ((null !== $event->getValue()) || !$this->scopeDeterminator->currentScopeIsBackend()) { + return; + } + + $environment = $event->getEnvironment(); + $property = $environment + ->getDataDefinition() + ->getPropertiesDefinition() + ->getProperty($event->getGroupField()); + + // No property? Get out! + if (!$property) { + $event->setValue('-'); + return; + } + + $value = $this->formatGroupHeader( + $environment, + $event->getModel(), + $property, + $event->getGroupingMode(), + $event->getGroupingLength() + ); + + if (null !== $value) { + $event->setValue($value); + } + } + + /** + * Get the group header. + * + * @param EnvironmentInterface $environment The environment. + * @param ModelInterface $model The model. + * @param PropertyInterface $property The property. + * @param int $groupingMode The grouping mode. + * @param int $groupingLength The grouping length. + * + * @return string + */ + protected function formatGroupHeader( + EnvironmentInterface $environment, + ModelInterface $model, + PropertyInterface $property, + $groupingMode, + $groupingLength + ) { + $evaluation = $property->getExtra(); + + if (isset($evaluation['multiple']) && !$evaluation['multiple'] && ('checkbox' === $property->getWidgetType())) { + return $this->formatCheckboxOptionLabel($model->getProperty($property->getName())); + } + if (GroupAndSortingInformationInterface::GROUP_NONE !== $groupingMode) { + return $this->formatByGroupingMode($groupingMode, $groupingLength, $environment, $property, $model); + } + + $value = ViewHelpers::getReadableFieldValue($environment, $property, $model); + + if (isset($evaluation['reference'])) { + $remoteNew = $evaluation['reference'][$value]; + } elseif (\array_is_assoc($property->getOptions())) { + $options = $property->getOptions(); + $remoteNew = $options[$value]; + } else { + $remoteNew = $value; + } + + if (\is_array($remoteNew)) { + $remoteNew = $remoteNew[0]; + } + + if (empty($remoteNew)) { + $remoteNew = '-'; + } + + return $remoteNew; + } + + /** + * Format the grouping header for a checkbox option. + * + * @param string $value The given value. + * + * @return string + */ + private function formatCheckboxOptionLabel($value) + { + return ('' !== $value) + ? \ucfirst($this->translator->translate('MSC.yes')) + : \ucfirst($this->translator->translate('MSC.no')); + } + + /** + * Format the group header by the grouping mode. + * + * @param int $groupingMode The grouping mode. + * @param int $groupingLength The grouping length. + * @param EnvironmentInterface $environment The environment. + * @param PropertyInterface $property The current property definition. + * @param ModelInterface $model The current data model. + * + * @return string + */ + private function formatByGroupingMode( + $groupingMode, + $groupingLength, + EnvironmentInterface $environment, + PropertyInterface $property, + ModelInterface $model + ) { + switch ($groupingMode) { + case GroupAndSortingInformationInterface::GROUP_CHAR: + return $this->formatByCharGrouping( + ViewHelpers::getReadableFieldValue($environment, $property, $model), + $groupingLength + ); + + case GroupAndSortingInformationInterface::GROUP_DAY: + return $this->formatByDayGrouping((int) $model->getProperty($property->getName())); + + case GroupAndSortingInformationInterface::GROUP_MONTH: + return $this->formatByMonthGrouping((int) $model->getProperty($property->getName())); + + case GroupAndSortingInformationInterface::GROUP_YEAR: + return $this->formatByYearGrouping((int) $model->getProperty($property->getName())); + + default: + return ViewHelpers::getReadableFieldValue($environment, $property, $model); + } + } + + /** + * Format a value for char grouping. + * + * @param string $value The value. + * @param int $groupingLength The group length. + * + * @return string + */ + private function formatByCharGrouping($value, $groupingLength) + { + if ('' === $value) { + return '-'; + } + + return mb_strtoupper(mb_substr($value, 0, $groupingLength ?: null)); + } + + /** + * Render a grouping header for day. + * + * @param int $value The value. + * + * @return string + */ + private function formatByDayGrouping($value) + { + $value = $this->getTimestamp($value); + if ('' === $value) { + return '-'; + } + $event = new ParseDateEvent($value, Config::get('dateFormat')); + $this->dispatcher->dispatch($event, ContaoEvents::DATE_PARSE); + + return $event->getResult(); + } + + /** + * Render a grouping header for month. + * + * @param int $value The value. + * + * @return string + */ + private function formatByMonthGrouping($value) + { + $value = $this->getTimestamp($value); + if ('' === $value) { + return '-'; + } + $event = new ParseDateEvent($value, 'F Y'); + $this->dispatcher->dispatch($event, ContaoEvents::DATE_PARSE); + + return $event->getResult(); + } + + /** + * Render a grouping header for year. + * + * @param int $value The value. + * + * @return string + */ + private function formatByYearGrouping($value) + { + $value = $this->getTimestamp($value); + if ('' === $value) { + return '-'; + } + + return date('Y', $value); + } + + /** + * Make sure a timestamp is returned. + * + * @param int|\DateTime $value The given date. + * + * @return int + */ + private function getTimestamp($value) + { + return ($value instanceof \DateTime) ? $value->getTimestamp() : $value; + } +} diff --git a/src/Contao/View/Contao2BackendView/Subscriber/MultipleHandlerSubscriber.php b/src/Contao/View/Contao2BackendView/Subscriber/MultipleHandlerSubscriber.php index 7e422ff8..500573b9 100644 --- a/src/Contao/View/Contao2BackendView/Subscriber/MultipleHandlerSubscriber.php +++ b/src/Contao/View/Contao2BackendView/Subscriber/MultipleHandlerSubscriber.php @@ -117,8 +117,10 @@ public function prepareGlobalAllButton(ActionEvent $event) public function deactivateGlobalButton(ActionEvent $event) { $allowedAction = ['selectModelAll', 'selectPropertyAll', 'editAll', 'overrideAll']; - if (!$this->scopeDeterminator->currentScopeIsBackend() - || !\in_array($event->getAction()->getName(), $allowedAction)) { + if ( + !$this->scopeDeterminator->currentScopeIsBackend() + || !\in_array($event->getAction()->getName(), $allowedAction) + ) { return; } @@ -151,7 +153,8 @@ public function handleOriginalOptions(GetOptionsEvent $event) { $environment = $event->getEnvironment(); - if (!$this->scopeDeterminator->currentScopeIsBackend() + if ( + !$this->scopeDeterminator->currentScopeIsBackend() || ('select' !== $environment->getInputProvider()->getParameter('act')) || ('edit' !== $environment->getInputProvider()->getParameter('select')) ) { @@ -160,7 +163,8 @@ public function handleOriginalOptions(GetOptionsEvent $event) $model = $event->getModel(); - if (!($propertyName = $this->getOriginalPropertyName($event->getPropertyName(), ModelId::fromModel($model))) + if ( + !($propertyName = $this->getOriginalPropertyName($event->getPropertyName(), ModelId::fromModel($model))) || !$model->getProperty($propertyName) ) { return; @@ -198,7 +202,8 @@ public function handleOriginalWidget(BuildWidgetEvent $event) { $environment = $event->getEnvironment(); - if (!$this->scopeDeterminator->currentScopeIsBackend() + if ( + !$this->scopeDeterminator->currentScopeIsBackend() || ('select' !== $environment->getInputProvider()->getParameter('act')) || ('edit' !== $environment->getInputProvider()->getParameter('select')) ) { @@ -213,7 +218,8 @@ public function handleOriginalWidget(BuildWidgetEvent $event) $modelId = ModelId::fromModel($model); $originalPropertyName = $this->getOriginalPropertyName($event->getProperty()->getName(), $modelId); - if ((null === $originalPropertyName) + if ( + (null === $originalPropertyName) || ((null !== $originalPropertyName) && (false === $properties->hasProperty($originalPropertyName))) ) { return; diff --git a/src/Contao/View/Contao2BackendView/Subscriber/RichTextFileUuidSubscriber.php b/src/Contao/View/Contao2BackendView/Subscriber/RichTextFileUuidSubscriber.php index 730d8de4..63d5d33d 100644 --- a/src/Contao/View/Contao2BackendView/Subscriber/RichTextFileUuidSubscriber.php +++ b/src/Contao/View/Contao2BackendView/Subscriber/RichTextFileUuidSubscriber.php @@ -97,9 +97,7 @@ public function convertFileSourceToUuid(EncodePropertyValueFromWidgetEvent $even $property = $propertiesDefinition->getProperty($event->getProperty()); - if (!\array_key_exists('rte', $property->getExtra()) - || (0 !== \strpos($property->getExtra()['rte'], 'tiny')) - ) { + if (!\array_key_exists('rte', $property->getExtra()) || (0 !== \strpos($property->getExtra()['rte'], 'tiny'))) { return; } @@ -126,9 +124,7 @@ public function convertUuidToFileSource(DecodePropertyValueForWidgetEvent $event $property = $propertiesDefinition->getProperty($event->getProperty()); - if (!\array_key_exists('rte', $property->getExtra()) - || \strpos($property->getExtra()['rte'], 'tiny') !== 0 - ) { + if (!\array_key_exists('rte', $property->getExtra()) || \strpos($property->getExtra()['rte'], 'tiny') !== 0) { return; } diff --git a/src/Contao/View/Contao2BackendView/Subscriber/WidgetBuilder.php b/src/Contao/View/Contao2BackendView/Subscriber/WidgetBuilder.php index 1851a5c9..6996e1e6 100644 --- a/src/Contao/View/Contao2BackendView/Subscriber/WidgetBuilder.php +++ b/src/Contao/View/Contao2BackendView/Subscriber/WidgetBuilder.php @@ -194,7 +194,8 @@ private function isGetOptionsAllowed(PropertyInterface $property): bool $propExtra = $property->getExtra(); // Check the overwrite param. - if (\is_array($propExtra) + if ( + \is_array($propExtra) && \array_key_exists('fetchOptions', $propExtra) && (true === $propExtra['fetchOptions']) ) { @@ -395,7 +396,8 @@ public function buildWidget( PropertyInterface $property, ModelInterface $model ) { - if (static::$scopeDeterminator->currentScopeIsUnknown() + if ( + static::$scopeDeterminator->currentScopeIsUnknown() || !static::$scopeDeterminator->currentScopeIsBackend() ) { throw new DcGeneralRuntimeException( @@ -445,7 +447,8 @@ private function valueToWidget(ModelInterface $model, PropertyInterface $propert $propExtra = $property->getExtra(); - if ((0 === (int) $value) + if ( + (0 === (int) $value) && \is_numeric($value) && empty($propExtra['mandatory']) && (isset($propExtra['rgxp']) && \in_array($propExtra['rgxp'], ['date', 'time', 'datim'])) @@ -505,7 +508,8 @@ private function prepareWidgetAttributes(ModelInterface $model, PropertyInterfac $environment->getEventDispatcher()->dispatch($event, ContaoEvents::WIDGET_GET_ATTRIBUTES_FROM_DCA); $prepareAttributes = $event->getResult(); - if (('checkbox' === $widgetConfig['inputType']) + if ( + ('checkbox' === $widgetConfig['inputType']) && isset($widgetConfig['eval']['submitOnChange']) && $widgetConfig['eval']['submitOnChange'] && isset($GLOBALS['TL_DCA'][$defName]['subpalettes']) @@ -530,7 +534,11 @@ private function prepareWidgetAttributes(ModelInterface $model, PropertyInterfac */ private function setPropExtraDisabled(PropertyInterface $property, array $propExtra): array { - if (isset($propExtra['readonly']) && $propExtra['readonly'] && \in_array($property->getWidgetType(), ['checkbox', 'select', 'radio'], true)) { + if ( + isset($propExtra['readonly']) + && $propExtra['readonly'] + && \in_array($property->getWidgetType(), ['checkbox', 'select', 'radio'], true) + ) { $propExtra['disabled'] = true; unset($propExtra['chosen']); } diff --git a/src/Contao/View/Contao2BackendView/TreePicker.php b/src/Contao/View/Contao2BackendView/TreePicker.php index 07288933..991ec574 100644 --- a/src/Contao/View/Contao2BackendView/TreePicker.php +++ b/src/Contao/View/Contao2BackendView/TreePicker.php @@ -855,9 +855,7 @@ public function generateAjax() { $input = $this->getEnvironment()->getInputProvider(); - if ($input->hasValue('action') - && ('DcGeneralLoadSubTree' === $input->getValue('action')) - ) { + if ($input->hasValue('action') && ('DcGeneralLoadSubTree' === $input->getValue('action'))) { $provider = $input->getValue('providerName'); $rootId = $input->getValue('id'); $this->getEnvironment()->getSessionStorage()->set( @@ -1468,7 +1466,8 @@ private function determineParentsOfValues() */ private function handleInputNameForEditAll() { - if (('select' !== $this->getEnvironment()->getInputProvider()->getParameter('act')) + if ( + ('select' !== $this->getEnvironment()->getInputProvider()->getParameter('act')) && ('edit' !== $this->getEnvironment()->getInputProvider()->getParameter('select')) && ('edit' !== $this->getEnvironment()->getInputProvider()->getParameter('mode')) ) { diff --git a/src/Contao/View/Contao2BackendView/TreeView.php b/src/Contao/View/Contao2BackendView/TreeView.php index d7fa49f6..5484c642 100644 --- a/src/Contao/View/Contao2BackendView/TreeView.php +++ b/src/Contao/View/Contao2BackendView/TreeView.php @@ -223,8 +223,7 @@ protected function loadParentModel() $pid = ModelId::fromSerialized($parentId); - if (!($parentProvider = $environment->getDataProvider($pid->getDataProviderName())) - ) { + if (!($parentProvider = $environment->getDataProvider($pid->getDataProviderName()))) { throw new DcGeneralRuntimeException( 'TreeView needs a proper parent data provider defined, somehow none is defined?', 1 @@ -514,9 +513,7 @@ protected function viewTree($collection) protected function formActionForSelect(ContaoBackendViewTemplate $template) { $environment = $this->getEnvironment(); - if (!$template->get('select') - || ('select' !== $environment->getInputProvider()->getParameter('act')) - ) { + if (!$template->get('select') || ('select' !== $environment->getInputProvider()->getParameter('act'))) { return; } diff --git a/src/Contao/View/Contao2BackendView/ViewHelpers.php b/src/Contao/View/Contao2BackendView/ViewHelpers.php index 8dd924dd..c92d088e 100644 --- a/src/Contao/View/Contao2BackendView/ViewHelpers.php +++ b/src/Contao/View/Contao2BackendView/ViewHelpers.php @@ -158,7 +158,8 @@ public static function getGroupingMode(EnvironmentInterface $environment) $sorting = static::getCurrentSorting($environment); // If no sorting defined, exit. - if ((!$sorting) + if ( + (!$sorting) || (!$sorting->getCount()) || $sorting->get(0)->getSortingMode() === GroupAndSortingInformationInterface::SORT_RANDOM ) { diff --git a/src/Contao/View/Contao2BackendView/Widget/FileTree.php b/src/Contao/View/Contao2BackendView/Widget/FileTree.php index 5a450f46..6b4b82ee 100644 --- a/src/Contao/View/Contao2BackendView/Widget/FileTree.php +++ b/src/Contao/View/Contao2BackendView/Widget/FileTree.php @@ -388,7 +388,8 @@ protected function renderIcon($model, $imagesOnly = false, $downloads = false) */ private function generateGalleryImage(File $file, $info) { - if ($file->viewWidth && $file->viewHeight + if ( + $file->viewWidth && $file->viewHeight && ($file->isSvgImage || (($file->height <= Config::get('gdMaxImgHeight')) && ($file->width <= Config::get('gdMaxImgWidth')) @@ -396,7 +397,8 @@ private function generateGalleryImage(File $file, $info) ) ) { // Inline the image if no preview image will be generated (see #636). - if ($file->height !== null && $file->height <= $this->thumbnailHeight + if ( + $file->height !== null && $file->height <= $this->thumbnailHeight && $file->width !== null && $file->width <= $this->thumbnailWidth ) { $image = $file->dataUri; diff --git a/src/ContaoManager/Plugin.php b/src/ContaoManager/Plugin.php index e1914fb1..a71c8568 100644 --- a/src/ContaoManager/Plugin.php +++ b/src/ContaoManager/Plugin.php @@ -60,7 +60,7 @@ public function getBundles(ParserInterface $parser) public function getRouteCollection(LoaderResolverInterface $resolver, KernelInterface $kernel) { return $resolver - ->resolve(__DIR__.'/../Resources/config/routing.yml') - ->load(__DIR__.'/../Resources/config/routing.yml'); + ->resolve(__DIR__ . '/../Resources/config/routing.yml') + ->load(__DIR__ . '/../Resources/config/routing.yml'); } } diff --git a/src/Controller/Ajax.php b/src/Controller/Ajax.php index e8d9a344..e32f2cbb 100644 --- a/src/Controller/Ajax.php +++ b/src/Controller/Ajax.php @@ -208,35 +208,39 @@ public function executePostActions(DataContainerInterface $container) $action = $this->getEnvironment()->getInputProvider()->getValue('action'); - if (\in_array( - $action, - [ - // This is impossible to handle generically in DcGeneral. - 'toggleFeatured', - // DcGeneral handles sub palettes differently. - 'toggleSubpalette' - ] - )) { + if ( + \in_array( + $action, + [ + // This is impossible to handle generically in DcGeneral. + 'toggleFeatured', + // DcGeneral handles sub palettes differently. + 'toggleSubpalette' + ] + ) + ) { return; } - if (\in_array( - $action, - [ - // Load nodes of the page structure tree. Compatible between 2.X and 3.X. - 'loadStructure', - // Load nodes of the file manager tree. - 'loadFileManager', - // Load nodes of the page tree. - 'loadPagetree', - // Load nodes of the file tree. - 'loadFiletree', - // Reload the page/file picker. - 'reloadPagetree', - 'reloadFiletree', - 'setLegendState' - ] - )) { + if ( + \in_array( + $action, + [ + // Load nodes of the page structure tree. Compatible between 2.X and 3.X. + 'loadStructure', + // Load nodes of the file manager tree. + 'loadFileManager', + // Load nodes of the page tree. + 'loadPagetree', + // Load nodes of the file tree. + 'loadFiletree', + // Reload the page/file picker. + 'reloadPagetree', + 'reloadFiletree', + 'setLegendState' + ] + ) + ) { $this->{$action}(); return; } diff --git a/src/Controller/Ajax3X.php b/src/Controller/Ajax3X.php index 9725ca51..3fc527f1 100644 --- a/src/Controller/Ajax3X.php +++ b/src/Controller/Ajax3X.php @@ -293,9 +293,7 @@ private function getFieldName() return $fieldName; } - if (('select' !== $inputProvider->getParameter('act')) - && ('edit' !== $inputProvider->getParameter('mode')) - ) { + if (('select' !== $inputProvider->getParameter('act')) && ('edit' !== $inputProvider->getParameter('mode'))) { return $fieldName; } @@ -337,9 +335,7 @@ private function generateWidget(Widget $widget) $environment = $this->getEnvironment(); $inputProvider = $environment->getInputProvider(); - if (('select' !== $inputProvider->getParameter('act')) - && ('edit' !== $inputProvider->getParameter('mode')) - ) { + if (('select' !== $inputProvider->getParameter('act')) && ('edit' !== $inputProvider->getParameter('mode'))) { echo $widget->parse(); return; diff --git a/src/Controller/BackendTreeController.php b/src/Controller/BackendTreeController.php index d6f25fe5..e98d675c 100644 --- a/src/Controller/BackendTreeController.php +++ b/src/Controller/BackendTreeController.php @@ -246,8 +246,7 @@ private function runBackendTreeToggle(Request $request) */ private function runBackendTreeUpdate(Request $request) { - if ((false === (bool) $request->request->count()) - && (false === $request->isXmlHttpRequest())) { + if ((false === (bool) $request->request->count()) && (false === $request->isXmlHttpRequest())) { throw new BadRequestHttpException('This request isn`t from type ajax.'); } @@ -284,7 +283,8 @@ private function runBackendTreeUpdate(Request $request) $values = []; // Clean keys the have empty value. foreach ($widgetValue as $index => $value) { - if (empty($value) + if ( + empty($value) // The first key entry has the value on, if the checkbox for all checked. || ((0 === $index) && ('on' === $value)) ) { diff --git a/src/Controller/DefaultController.php b/src/Controller/DefaultController.php index 03e3ba57..baa5683f 100644 --- a/src/Controller/DefaultController.php +++ b/src/Controller/DefaultController.php @@ -368,7 +368,8 @@ private function handleClonedModelProperty( } // Check uniqueness. - if (isset($extra['unique']) + if ( + isset($extra['unique']) && (true === $extra['unique']) && !$dataProvider->isUniqueValue($propName, $model->getProperty($propName)) ) { @@ -852,7 +853,8 @@ private function processPasteTopWithoutReference( ModelIdInterface $into = null, ModelIdInterface $parent = null ) { - if ($models->count() + if ( + $models->count() && (($after && (0 === (int) $after->getId())) || ($into && (0 === (int) $into->getId()))) ) { @@ -1060,16 +1062,15 @@ public function pasteAfter(ModelInterface $previousModel, CollectionInterface $m } $environment = $this->getEnvironment(); - if (\in_array( - $environment - ->getDataDefinition() - ->getBasicDefinition() - ->getMode(), - [ - BasicDefinitionInterface::MODE_HIERARCHICAL, - BasicDefinitionInterface::MODE_PARENTEDLIST - ] - )) { + if ( + \in_array( + $environment->getDataDefinition()->getBasicDefinition()->getMode(), + [ + BasicDefinitionInterface::MODE_HIERARCHICAL, + BasicDefinitionInterface::MODE_PARENTEDLIST + ] + ) + ) { if (!$this->relationshipManager->isRoot($previousModel)) { $parentModel = $this->modelCollector->searchParentOf($previousModel); $parentName = $parentModel->getProviderName(); diff --git a/src/Controller/ModelCollector.php b/src/Controller/ModelCollector.php index fe60a8e1..5babbb76 100644 --- a/src/Controller/ModelCollector.php +++ b/src/Controller/ModelCollector.php @@ -496,7 +496,8 @@ private function searchParentOfInHierarchical(ModelInterface $model) foreach ($this->relationships->getChildConditions() as $condition) { // Skip conditions where the destination is not the provider - if ($this->defaultProviderName !== $condition->getDestinationName() + if ( + $this->defaultProviderName !== $condition->getDestinationName() || $this->defaultProviderName !== $condition->getSourceName() ) { continue; diff --git a/src/Controller/TreeCollector.php b/src/Controller/TreeCollector.php index 04b5b49b..c0b34537 100644 --- a/src/Controller/TreeCollector.php +++ b/src/Controller/TreeCollector.php @@ -174,7 +174,8 @@ private function getChildrenOfModel($dataProvider, $model, $childCondition) ->setIdOnly(true) ); - if (($childIds instanceof CollectionInterface && !$childIds->count()) + if ( + ($childIds instanceof CollectionInterface && !$childIds->count()) || (\is_array($childIds) && !\count($childIds)) ) { return null; @@ -282,10 +283,12 @@ private function addParentFilter(ConfigInterface $config, ModelInterface $parent } // Apply parent filtering, do this only for root elements. - if ($parentCondition = $definition->getModelRelationshipDefinition()->getChildCondition( - $basicDefinition->getParentDataProvider(), - $basicDefinition->getRootDataProvider() - )) { + if ( + $parentCondition = $definition->getModelRelationshipDefinition()->getChildCondition( + $basicDefinition->getParentDataProvider(), + $basicDefinition->getRootDataProvider() + ) + ) { $baseFilter = $config->getFilter(); $filter = $parentCondition->getFilter($parentModel); diff --git a/src/DC/General.php b/src/DC/General.php index f007e61e..2db23ec3 100644 --- a/src/DC/General.php +++ b/src/DC/General.php @@ -139,7 +139,8 @@ private function getTranslator() */ private function checkAjaxCall() { - if (!empty($_POST) + if ( + !empty($_POST) && (isset($_SERVER['HTTP_X_REQUESTED_WITH']) && 'XMLHttpRequest' === $_SERVER['HTTP_X_REQUESTED_WITH']) ) { @@ -159,7 +160,8 @@ private function checkAjaxCall() */ protected function getTablenameCallback($tableName) { - if (isset($GLOBALS['TL_DCA'][$tableName]['config']['tablename_callback']) + if ( + isset($GLOBALS['TL_DCA'][$tableName]['config']['tablename_callback']) && \is_array($GLOBALS['TL_DCA'][$tableName]['config']['tablename_callback']) ) { foreach ($GLOBALS['TL_DCA'][$tableName]['config']['tablename_callback'] as $callback) { diff --git a/src/Data/DefaultCollection.php b/src/Data/DefaultCollection.php index 35e75c0e..79fc13ad 100644 --- a/src/Data/DefaultCollection.php +++ b/src/Data/DefaultCollection.php @@ -302,7 +302,8 @@ public function intersect($collection) foreach ($this as $localModel) { /** @var ModelInterface $otherModel */ foreach ($collection as $otherModel) { - if (($localModel->getProviderName() === $otherModel->getProviderName()) + if ( + ($localModel->getProviderName() === $otherModel->getProviderName()) && ($localModel->getId() === $otherModel->getId()) ) { $intersection->push($localModel); @@ -347,7 +348,8 @@ public function diff($collection) foreach ($this as $localModel) { /** @var ModelInterface $otherModel */ foreach ($collection as $otherModel) { - if (($localModel->getProviderName() === $otherModel->getProviderName()) + if ( + ($localModel->getProviderName() === $otherModel->getProviderName()) && ($localModel->getId() === $otherModel->getId()) ) { continue; @@ -372,7 +374,8 @@ public function isSubsetOf($collection) foreach ($this as $localModel) { /** @var ModelInterface $otherModel */ foreach ($collection as $otherModel) { - if (($localModel->getProviderName() === $otherModel->getProviderName()) + if ( + ($localModel->getProviderName() === $otherModel->getProviderName()) && ($localModel->getId() === $otherModel->getId()) ) { continue; diff --git a/src/Data/DefaultDataProvider.php b/src/Data/DefaultDataProvider.php index 8dbf247a..9e9a723d 100644 --- a/src/Data/DefaultDataProvider.php +++ b/src/Data/DefaultDataProvider.php @@ -568,7 +568,8 @@ private function sortingPrefixer(ConfigInterface $config) private function filterPrefixer(array &$filter) { foreach ($filter as &$child) { - if (\array_key_exists('property', $child) + if ( + \array_key_exists('property', $child) && (false === \strpos($child['property'], $this->source . '.')) && $this->fieldExists($child['property']) ) { @@ -591,9 +592,7 @@ private function filterPrefixer(array &$filter) private function fieldPrefixer(array &$fields) { foreach ($fields as $index => $property) { - if (0 === \strpos($property, $this->source . '.') - || !$this->fieldExists($property) - ) { + if (0 === \strpos($property, $this->source . '.') || !$this->fieldExists($property)) { continue; } diff --git a/src/Data/DefaultEditInformation.php b/src/Data/DefaultEditInformation.php index ebeae0f9..6655e2fd 100644 --- a/src/Data/DefaultEditInformation.php +++ b/src/Data/DefaultEditInformation.php @@ -93,7 +93,7 @@ public function setModelError(ModelInterface $model, array $error, PropertyInter $this->modelErrors[$modelId->getSerialized()] = []; } - if(isset($this->modelErrors[$modelId->getSerialized()][$property->getName()])){ + if (isset($this->modelErrors[$modelId->getSerialized()][$property->getName()])) { $this->modelErrors[$modelId->getSerialized()][$property->getName()] = \array_merge( (array) $this->modelErrors[$modelId->getSerialized()][$property->getName()], $error @@ -101,7 +101,6 @@ public function setModelError(ModelInterface $model, array $error, PropertyInter } else { $this->modelErrors[$modelId->getSerialized()][$property->getName()] = $error; } - } /** diff --git a/src/Data/ModelInterface.php b/src/Data/ModelInterface.php index cd8bab8d..6bfe1251 100644 --- a/src/Data/ModelInterface.php +++ b/src/Data/ModelInterface.php @@ -34,67 +34,67 @@ interface ModelInterface extends \IteratorAggregate /** * Name of the parent provider. */ - const PARENT_PROVIDER_NAME = 'ptable'; + public const PARENT_PROVIDER_NAME = 'ptable'; /** * Id value of the parent model. */ - const PARENT_ID = 'pid'; + public const PARENT_ID = 'pid'; /** * State if we have children. */ - const HAS_CHILDREN = 'dc_gen_tv_children'; + public const HAS_CHILDREN = 'dc_gen_tv_children'; /** * If the children shall be shown (i.e. unfolded in tree mode). */ - const SHOW_CHILDREN = 'dc_gen_tv_open'; + public const SHOW_CHILDREN = 'dc_gen_tv_open'; /** * All child collections. */ - const CHILD_COLLECTIONS = 'dc_gen_children_collection'; + public const CHILD_COLLECTIONS = 'dc_gen_children_collection'; /** * Meta name for the model operation buttons. */ - const OPERATION_BUTTONS = '%buttons%'; + public const OPERATION_BUTTONS = '%buttons%'; /** * Meta name for the model label arguments. */ - const LABEL_ARGS = '%args%'; + public const LABEL_ARGS = '%args%'; /** * Meta name for the model label (sprintf string). */ - const LABEL_VALUE = '%content%'; + public const LABEL_VALUE = '%content%'; /** * Meta name for the model group header. */ - const GROUP_HEADER = '%header%'; + public const GROUP_HEADER = '%header%'; /** * Meta name for the model group value. */ - const GROUP_VALUE = '%group%'; + public const GROUP_VALUE = '%group%'; /** * Meta name for the model label class. */ - const CSS_CLASS = '%class%'; + public const CSS_CLASS = '%class%'; /** * Meta name for the model label class. */ - const CSS_ROW_CLASS = '%rowClass%'; + public const CSS_ROW_CLASS = '%rowClass%'; /** * State if the model is changed */ - const IS_CHANGED = 'isChanged'; + public const IS_CHANGED = 'isChanged'; /** * Copy this model, without the id. diff --git a/src/Data/ModelManipulator.php b/src/Data/ModelManipulator.php index 22410aeb..d840d6ec 100644 --- a/src/Data/ModelManipulator.php +++ b/src/Data/ModelManipulator.php @@ -86,7 +86,8 @@ public static function updateModelFromPropertyBag( public static function sanitizeValue(PropertyInterface $property, $value) { // If value empty, then override with empty value in property (if it has any). - if (empty($value) + if ( + empty($value) && ($property instanceof EmptyValueAwarePropertyInterface) && $property->hasEmptyValue() ) { diff --git a/src/Data/NoOpDataProvider.php b/src/Data/NoOpDataProvider.php index 40126831..6beb8f0a 100644 --- a/src/Data/NoOpDataProvider.php +++ b/src/Data/NoOpDataProvider.php @@ -215,7 +215,8 @@ public function sameModels($firstModel, $secondModel) $propertyNames = \array_unique(\array_merge(\array_keys($firstProperties), \array_keys($secondProperties))); foreach ($propertyNames as $propertyName) { - if (!\array_key_exists($propertyName, $firstProperties) || + if ( + !\array_key_exists($propertyName, $firstProperties) || !\array_key_exists($propertyName, $secondProperties) || $firstProperties[$propertyName] !== $secondProperties[$propertyName] ) { diff --git a/src/Data/PropertyValueBag.php b/src/Data/PropertyValueBag.php index 5e726430..01fee7b3 100644 --- a/src/Data/PropertyValueBag.php +++ b/src/Data/PropertyValueBag.php @@ -31,7 +31,6 @@ */ class PropertyValueBag implements PropertyValueBagInterface { - /** * All properties and its values in this bag. * diff --git a/src/DataDefinition/Builder/AbstractEventDrivenDataDefinitionBuilder.php b/src/DataDefinition/Builder/AbstractEventDrivenDataDefinitionBuilder.php index a10fced1..2fc3bb39 100644 --- a/src/DataDefinition/Builder/AbstractEventDrivenDataDefinitionBuilder.php +++ b/src/DataDefinition/Builder/AbstractEventDrivenDataDefinitionBuilder.php @@ -36,8 +36,10 @@ abstract class AbstractEventDrivenDataDefinitionBuilder implements DataDefinitio /** * Priority of the listener. * Just here for sanity, must be overwritten by implementation. + * + * @deprecated Should not be used at all. */ - const PRIORITY = null; + public const PRIORITY = null; /** * The event dispatcher currently calling. diff --git a/src/DataDefinition/ConditionChainInterface.php b/src/DataDefinition/ConditionChainInterface.php index 6da51d4e..51d2c929 100644 --- a/src/DataDefinition/ConditionChainInterface.php +++ b/src/DataDefinition/ConditionChainInterface.php @@ -29,12 +29,12 @@ interface ConditionChainInterface extends ConditionInterface /** * All conditions must match. */ - const AND_CONJUNCTION = 'AND'; + public const AND_CONJUNCTION = 'AND'; /** * Only one condition must match. */ - const OR_CONJUNCTION = 'OR'; + public const OR_CONJUNCTION = 'OR'; /** * Clear the chain. diff --git a/src/DataDefinition/Definition/BasicDefinitionInterface.php b/src/DataDefinition/Definition/BasicDefinitionInterface.php index e0be7ac6..d50ffbba 100644 --- a/src/DataDefinition/Definition/BasicDefinitionInterface.php +++ b/src/DataDefinition/Definition/BasicDefinitionInterface.php @@ -30,22 +30,22 @@ interface BasicDefinitionInterface extends DefinitionInterface /** * The name of the definition. */ - const NAME = 'basic'; + public const NAME = 'basic'; /** * Flat mode. All models are on the same hierarchical level. No root conditions are defined. */ - const MODE_FLAT = 0; + public const MODE_FLAT = 0; /** * Parented list mode. The models are children of a parent model. */ - const MODE_PARENTEDLIST = 1; + public const MODE_PARENTEDLIST = 1; /** * Hierarchical mode. The models span over various levels. */ - const MODE_HIERARCHICAL = 2; + public const MODE_HIERARCHICAL = 2; /** * Set the mode the data definition is in. diff --git a/src/DataDefinition/Definition/DataProviderDefinitionInterface.php b/src/DataDefinition/Definition/DataProviderDefinitionInterface.php index 1d8a7ec6..61b93c42 100644 --- a/src/DataDefinition/Definition/DataProviderDefinitionInterface.php +++ b/src/DataDefinition/Definition/DataProviderDefinitionInterface.php @@ -28,7 +28,7 @@ */ interface DataProviderDefinitionInterface extends DefinitionInterface, \IteratorAggregate, \Countable, \ArrayAccess { - const NAME = 'dataProvider'; + public const NAME = 'dataProvider'; /** * Add a data provider information to the definition. diff --git a/src/DataDefinition/Definition/ModelRelationshipDefinitionInterface.php b/src/DataDefinition/Definition/ModelRelationshipDefinitionInterface.php index 97cb4f54..e1db7fef 100644 --- a/src/DataDefinition/Definition/ModelRelationshipDefinitionInterface.php +++ b/src/DataDefinition/Definition/ModelRelationshipDefinitionInterface.php @@ -33,7 +33,7 @@ interface ModelRelationshipDefinitionInterface extends DefinitionInterface /** * The name of the definition. */ - const NAME = 'model-relationships'; + public const NAME = 'model-relationships'; /** * Set the root condition for the current table. diff --git a/src/DataDefinition/Definition/PalettesDefinitionInterface.php b/src/DataDefinition/Definition/PalettesDefinitionInterface.php index fca2e480..082c3148 100644 --- a/src/DataDefinition/Definition/PalettesDefinitionInterface.php +++ b/src/DataDefinition/Definition/PalettesDefinitionInterface.php @@ -33,5 +33,5 @@ interface PalettesDefinitionInterface extends DefinitionInterface, PaletteCollec /** * The name of the definition. */ - const NAME = 'palettes'; + public const NAME = 'palettes'; } diff --git a/src/DataDefinition/Definition/PropertiesDefinitionInterface.php b/src/DataDefinition/Definition/PropertiesDefinitionInterface.php index 80665c07..d166d006 100644 --- a/src/DataDefinition/Definition/PropertiesDefinitionInterface.php +++ b/src/DataDefinition/Definition/PropertiesDefinitionInterface.php @@ -31,7 +31,7 @@ interface PropertiesDefinitionInterface extends DefinitionInterface, \IteratorAg /** * The name of the definition. */ - const NAME = 'properties'; + public const NAME = 'properties'; /** * Get all properties. diff --git a/src/DataDefinition/Definition/View/GroupAndSortingInformationInterface.php b/src/DataDefinition/Definition/View/GroupAndSortingInformationInterface.php index 52ab4e0f..a17f0c09 100644 --- a/src/DataDefinition/Definition/View/GroupAndSortingInformationInterface.php +++ b/src/DataDefinition/Definition/View/GroupAndSortingInformationInterface.php @@ -28,59 +28,59 @@ interface GroupAndSortingInformationInterface /** * Do not group. */ - const GROUP_NONE = 'none'; + public const GROUP_NONE = 'none'; /** * Group by characters, the max char count depend on the mode length * (which is 1 by default for char grouping). */ - const GROUP_CHAR = 'char'; + public const GROUP_CHAR = 'char'; /** * Group by digits, the max digit count depend on the mode length * (which is infinity by default for digit grouping).. */ - const GROUP_DIGIT = 'digit'; + public const GROUP_DIGIT = 'digit'; /** * Sort by day from datetime property. */ - const GROUP_DAY = 'day'; + public const GROUP_DAY = 'day'; /** * Sort by week day from datetime property. */ - const GROUP_WEEKDAY = 'weekday'; + public const GROUP_WEEKDAY = 'weekday'; /** * Sort by week of the year from datetime property. */ - const GROUP_WEEK = 'week'; + public const GROUP_WEEK = 'week'; /** * Sort by month from datetime property. */ - const GROUP_MONTH = 'month'; + public const GROUP_MONTH = 'month'; /** * Sort by year from datetime property. */ - const GROUP_YEAR = 'year'; + public const GROUP_YEAR = 'year'; /** * Sort ascending. */ - const SORT_ASC = 'asc'; + public const SORT_ASC = 'asc'; /** * Sort descending. */ - const SORT_DESC = 'desc'; + public const SORT_DESC = 'desc'; /** * Shuffle all records instead of sorting. */ - const SORT_RANDOM = 'random'; + public const SORT_RANDOM = 'random'; /** * Set the name of the property. diff --git a/src/DataDefinition/Palette/Builder/Event/AddConditionEvent.php b/src/DataDefinition/Palette/Builder/Event/AddConditionEvent.php index 9ecfceda..28509af2 100644 --- a/src/DataDefinition/Palette/Builder/Event/AddConditionEvent.php +++ b/src/DataDefinition/Palette/Builder/Event/AddConditionEvent.php @@ -33,7 +33,7 @@ */ class AddConditionEvent extends BuilderEvent { - const NAME = 'dc-general.data-definition.palette.builder.add-condition'; + public const NAME = 'dc-general.data-definition.palette.builder.add-condition'; /** * The condition that is being added. @@ -74,7 +74,8 @@ public function __construct($condition, $target, PaletteBuilder $paletteBuilder) */ public function setCondition($condition) { - if ((!$condition instanceof PaletteConditionInterface) + if ( + (!$condition instanceof PaletteConditionInterface) && (!$condition instanceof PropertyConditionInterface) ) { throw new DcGeneralInvalidArgumentException( diff --git a/src/DataDefinition/Palette/Builder/Event/CreateConditionEvent.php b/src/DataDefinition/Palette/Builder/Event/CreateConditionEvent.php index a3714376..0c2b5ef0 100644 --- a/src/DataDefinition/Palette/Builder/Event/CreateConditionEvent.php +++ b/src/DataDefinition/Palette/Builder/Event/CreateConditionEvent.php @@ -31,7 +31,7 @@ */ class CreateConditionEvent extends BuilderEvent { - const NAME = 'dc-general.data-definition.palette.builder.create-condition'; + public const NAME = 'dc-general.data-definition.palette.builder.create-condition'; /** * The condition being created. @@ -65,7 +65,8 @@ public function __construct($condition, PaletteBuilder $paletteBuilder) */ public function setCondition($condition) { - if ((!$condition instanceof PaletteConditionInterface) + if ( + (!$condition instanceof PaletteConditionInterface) && (!$condition instanceof PropertyConditionInterface) ) { throw new DcGeneralInvalidArgumentException( diff --git a/src/DataDefinition/Palette/Builder/Event/CreateDefaultPaletteConditionEvent.php b/src/DataDefinition/Palette/Builder/Event/CreateDefaultPaletteConditionEvent.php index 3f3f227c..08b47943 100644 --- a/src/DataDefinition/Palette/Builder/Event/CreateDefaultPaletteConditionEvent.php +++ b/src/DataDefinition/Palette/Builder/Event/CreateDefaultPaletteConditionEvent.php @@ -29,7 +29,7 @@ */ class CreateDefaultPaletteConditionEvent extends BuilderEvent { - const NAME = 'dc-general.data-definition.palette.builder.create-default-palette-condition'; + public const NAME = 'dc-general.data-definition.palette.builder.create-default-palette-condition'; /** * The default palette condition. diff --git a/src/DataDefinition/Palette/Builder/Event/CreateLegendEvent.php b/src/DataDefinition/Palette/Builder/Event/CreateLegendEvent.php index 8975851d..b5fb5b25 100644 --- a/src/DataDefinition/Palette/Builder/Event/CreateLegendEvent.php +++ b/src/DataDefinition/Palette/Builder/Event/CreateLegendEvent.php @@ -29,7 +29,7 @@ */ class CreateLegendEvent extends BuilderEvent { - const NAME = 'dc-general.data-definition.palette.builder.create-legend'; + public const NAME = 'dc-general.data-definition.palette.builder.create-legend'; /** * The legend that has been created. diff --git a/src/DataDefinition/Palette/Builder/Event/CreatePaletteCollectionEvent.php b/src/DataDefinition/Palette/Builder/Event/CreatePaletteCollectionEvent.php index 24c6ca40..44265518 100644 --- a/src/DataDefinition/Palette/Builder/Event/CreatePaletteCollectionEvent.php +++ b/src/DataDefinition/Palette/Builder/Event/CreatePaletteCollectionEvent.php @@ -29,7 +29,7 @@ */ class CreatePaletteCollectionEvent extends BuilderEvent { - const NAME = 'dc-general.data-definition.palette.builder.create-palette-collection'; + public const NAME = 'dc-general.data-definition.palette.builder.create-palette-collection'; /** * The palette collection that has been created. diff --git a/src/DataDefinition/Palette/Builder/Event/CreatePaletteConditionChainEvent.php b/src/DataDefinition/Palette/Builder/Event/CreatePaletteConditionChainEvent.php index b72def6c..0626ff95 100644 --- a/src/DataDefinition/Palette/Builder/Event/CreatePaletteConditionChainEvent.php +++ b/src/DataDefinition/Palette/Builder/Event/CreatePaletteConditionChainEvent.php @@ -29,7 +29,7 @@ */ class CreatePaletteConditionChainEvent extends BuilderEvent { - const NAME = 'dc-general.data-definition.palette.builder.create-palette-condition-chain'; + public const NAME = 'dc-general.data-definition.palette.builder.create-palette-condition-chain'; /** * The palette condition chain being created. diff --git a/src/DataDefinition/Palette/Builder/Event/CreatePaletteEvent.php b/src/DataDefinition/Palette/Builder/Event/CreatePaletteEvent.php index d0647cf3..6a0ce12d 100644 --- a/src/DataDefinition/Palette/Builder/Event/CreatePaletteEvent.php +++ b/src/DataDefinition/Palette/Builder/Event/CreatePaletteEvent.php @@ -29,7 +29,7 @@ */ class CreatePaletteEvent extends BuilderEvent { - const NAME = 'dc-general.data-definition.palette.builder.create-palette'; + public const NAME = 'dc-general.data-definition.palette.builder.create-palette'; /** * The palette that has been created. diff --git a/src/DataDefinition/Palette/Builder/Event/CreatePropertyConditionChainEvent.php b/src/DataDefinition/Palette/Builder/Event/CreatePropertyConditionChainEvent.php index 6158bbed..2fdfdac5 100644 --- a/src/DataDefinition/Palette/Builder/Event/CreatePropertyConditionChainEvent.php +++ b/src/DataDefinition/Palette/Builder/Event/CreatePropertyConditionChainEvent.php @@ -29,7 +29,7 @@ */ class CreatePropertyConditionChainEvent extends BuilderEvent { - const NAME = 'dc-general.data-definition.palette.builder.create-property-condition-chain'; + public const NAME = 'dc-general.data-definition.palette.builder.create-property-condition-chain'; /** * The property condition chain. diff --git a/src/DataDefinition/Palette/Builder/Event/CreatePropertyEvent.php b/src/DataDefinition/Palette/Builder/Event/CreatePropertyEvent.php index b2480a60..af176fec 100644 --- a/src/DataDefinition/Palette/Builder/Event/CreatePropertyEvent.php +++ b/src/DataDefinition/Palette/Builder/Event/CreatePropertyEvent.php @@ -29,7 +29,7 @@ */ class CreatePropertyEvent extends BuilderEvent { - const NAME = 'dc-general.data-definition.palette.builder.create-property'; + public const NAME = 'dc-general.data-definition.palette.builder.create-property'; /** * The property. diff --git a/src/DataDefinition/Palette/Builder/Event/CreatePropertyValueConditionEvent.php b/src/DataDefinition/Palette/Builder/Event/CreatePropertyValueConditionEvent.php index d11e7079..f2a36b9f 100644 --- a/src/DataDefinition/Palette/Builder/Event/CreatePropertyValueConditionEvent.php +++ b/src/DataDefinition/Palette/Builder/Event/CreatePropertyValueConditionEvent.php @@ -33,7 +33,7 @@ */ class CreatePropertyValueConditionEvent extends BuilderEvent { - const NAME = 'dc-general.data-definition.palette.builder.create-property-value-condition'; + public const NAME = 'dc-general.data-definition.palette.builder.create-property-value-condition'; /** * The property value condition. @@ -65,7 +65,8 @@ public function __construct($condition, PaletteBuilder $paletteBuilder) */ public function setPropertyValueCondition($condition) { - if (!($condition instanceof PalettePropertyValueCondition) + if ( + !($condition instanceof PalettePropertyValueCondition) && (!$condition instanceof PropertyValueCondition) ) { throw new DcGeneralInvalidArgumentException( diff --git a/src/DataDefinition/Palette/Builder/Event/FinishConditionEvent.php b/src/DataDefinition/Palette/Builder/Event/FinishConditionEvent.php index 8396c21c..f2879181 100644 --- a/src/DataDefinition/Palette/Builder/Event/FinishConditionEvent.php +++ b/src/DataDefinition/Palette/Builder/Event/FinishConditionEvent.php @@ -63,7 +63,8 @@ public function __construct($condition, PaletteBuilder $paletteBuilder) */ public function setCondition($condition) { - if ((!$condition instanceof PaletteConditionInterface) + if ( + (!$condition instanceof PaletteConditionInterface) && (!$condition instanceof PropertyConditionInterface) ) { throw new DcGeneralInvalidArgumentException( diff --git a/src/DataDefinition/Palette/Builder/Event/UsePropertyEvent.php b/src/DataDefinition/Palette/Builder/Event/UsePropertyEvent.php index f55c34a3..08da0888 100644 --- a/src/DataDefinition/Palette/Builder/Event/UsePropertyEvent.php +++ b/src/DataDefinition/Palette/Builder/Event/UsePropertyEvent.php @@ -29,7 +29,7 @@ */ class UsePropertyEvent extends BuilderEvent { - const NAME = 'dc-general.data-definition.palette.builder.use-property'; + public const NAME = 'dc-general.data-definition.palette.builder.use-property'; /** * The property. diff --git a/src/DataDefinition/Palette/Builder/PaletteBuilder.php b/src/DataDefinition/Palette/Builder/PaletteBuilder.php index 2e9dba15..e80c921f 100644 --- a/src/DataDefinition/Palette/Builder/PaletteBuilder.php +++ b/src/DataDefinition/Palette/Builder/PaletteBuilder.php @@ -987,7 +987,8 @@ protected function createPaletteConditionChain() */ protected function createPropertyConditionChain($conjunction = PropertyConditionChain::AND_CONJUNCTION) { - if (!($this->condition instanceof PropertyConditionChain) + if ( + !($this->condition instanceof PropertyConditionChain) || ($conjunction !== $this->condition->getConjunction()) ) { $previousCondition = $this->condition; diff --git a/src/DataDefinition/Palette/Condition/Property/DumpingPropertyCondition.php b/src/DataDefinition/Palette/Condition/Property/DumpingPropertyCondition.php index 2cb7b546..14798ea7 100644 --- a/src/DataDefinition/Palette/Condition/Property/DumpingPropertyCondition.php +++ b/src/DataDefinition/Palette/Condition/Property/DumpingPropertyCondition.php @@ -33,7 +33,6 @@ */ class DumpingPropertyCondition implements PropertyConditionInterface { - /** * The condition to dump. * diff --git a/src/Panel/AbstractElement.php b/src/Panel/AbstractElement.php index d96f51bf..41ac490f 100644 --- a/src/Panel/AbstractElement.php +++ b/src/Panel/AbstractElement.php @@ -31,7 +31,6 @@ */ abstract class AbstractElement implements PanelElementInterface { - /** * The panel this element is contained within. * diff --git a/src/View/ActionHandler/AbstractPropertyOverrideEditAllHandler.php b/src/View/ActionHandler/AbstractPropertyOverrideEditAllHandler.php index 75fa8961..894cf1a6 100644 --- a/src/View/ActionHandler/AbstractPropertyOverrideEditAllHandler.php +++ b/src/View/ActionHandler/AbstractPropertyOverrideEditAllHandler.php @@ -66,8 +66,10 @@ protected function handleSubmit(Action $action, EnvironmentInterface $environmen $sessionStorage = $environment->getSessionStorage(); $eventDispatcher = $environment->getEventDispatcher(); - if (('auto' === $inputProvider->getValue('SUBMIT_TYPE')) - || !$inputProvider->hasValue($this->getMode($action) . '_saveNback')) { + if ( + ('auto' === $inputProvider->getValue('SUBMIT_TYPE')) + || !$inputProvider->hasValue($this->getMode($action) . '_saveNback') + ) { return; } @@ -179,8 +181,7 @@ private function cloneCleanPropertyValueBag( $clonePropertyValueBag->resetPropertyValueErrors($propertyName); if ($this->ensurePropertyVisibleInModel($action, $propertyName, $model, $environment)) { - if (!\array_key_exists($propertyName, $sessionProperties) - ) { + if (!\array_key_exists($propertyName, $sessionProperties)) { $clonePropertyValueBag->setPropertyValue($propertyName, $model->getProperty($propertyName)); } @@ -707,8 +708,7 @@ protected function revertValuesByErrors( $revertModel->setId($revertModel->getId()); foreach ($properties as $property) { - if (('edit' === $this->getMode($action)) - && !\in_array($property->getName(), $modelErrors)) { + if (('edit' === $this->getMode($action)) && !\in_array($property->getName(), $modelErrors)) { continue; } diff --git a/src/View/ActionHandler/AbstractPropertyVisibilityHandler.php b/src/View/ActionHandler/AbstractPropertyVisibilityHandler.php index aec6f75c..0892fa25 100644 --- a/src/View/ActionHandler/AbstractPropertyVisibilityHandler.php +++ b/src/View/ActionHandler/AbstractPropertyVisibilityHandler.php @@ -545,7 +545,8 @@ private function matchInvisibleProperty( continue; } - if (isset($invisibleProperties[$condition->getPropertyName()]) + if ( + isset($invisibleProperties[$condition->getPropertyName()]) || !$propertiesDefinition->hasProperty($condition->getPropertyName() . '.dummy') ) { continue; @@ -639,13 +640,15 @@ private function matchParentInvisibleProperty( ); } - if (!\method_exists($condition, 'getPropertyName') + if ( + !\method_exists($condition, 'getPropertyName') || ($property->getName() !== $condition->getPropertyName()) ) { continue; } - if (isset($invisibleProperties[$legendProperty->getName()]) + if ( + isset($invisibleProperties[$legendProperty->getName()]) || !$propertiesDefinition->hasProperty($legendProperty->getName() . '.dummy') ) { continue; @@ -681,7 +684,8 @@ protected function getIntersectionModel(Action $action, EnvironmentInterface $en $idProperty = \method_exists($dataProvider, 'getIdProperty') ? $dataProvider->getIdProperty() : 'id'; foreach ((array) $session['intersectValues'] as $intersectProperty => $intersectValue) { - if (($idProperty === $intersectProperty) + if ( + ($idProperty === $intersectProperty) || !$propertiesDefinition->hasProperty($intersectProperty) || (false === $this->useIntersectValue( $intersectProperty, @@ -730,12 +734,12 @@ private function useIntersectValue( $propertiesDefinition = $environment->getDataDefinition()->getPropertiesDefinition(); $useIntersectValue = (bool) $defaultPalette; - if ($defaultPalette && !$propertiesDefinition->getProperty($intersectPropertyName)->getWidgetType() - ) { + if ($defaultPalette && !$propertiesDefinition->getProperty($intersectPropertyName)->getWidgetType()) { $useIntersectValue = true; } - if ($defaultPalette + if ( + $defaultPalette && (false === $useIntersectValue) && \in_array($intersectPropertyName, $legendPropertyNames) ) { diff --git a/tests/CcaDcGeneralBundleTest.php b/tests/CcaDcGeneralBundleTest.php index 60882645..a6194e37 100644 --- a/tests/CcaDcGeneralBundleTest.php +++ b/tests/CcaDcGeneralBundleTest.php @@ -31,7 +31,6 @@ */ class CcaDcGeneralBundleTest extends TestCase { - public function testBuild() { $passes = [ diff --git a/tests/Clipboard/ItemTest.php b/tests/Clipboard/ItemTest.php index 4c6b6091..be87448e 100644 --- a/tests/Clipboard/ItemTest.php +++ b/tests/Clipboard/ItemTest.php @@ -34,7 +34,7 @@ */ class ItemTest extends TestCase { - const TEST_PROVIDER = 'dummy-provider'; + public const TEST_PROVIDER = 'dummy-provider'; /** * Test the the Item requires an valid model id. diff --git a/tests/Clipboard/UnsavedItemTest.php b/tests/Clipboard/UnsavedItemTest.php index 87dec4a9..3a5c00bb 100644 --- a/tests/Clipboard/UnsavedItemTest.php +++ b/tests/Clipboard/UnsavedItemTest.php @@ -58,7 +58,7 @@ public function dataTestGetter() $modelId = ModelId::fromValues('parent', 'foo'); return [ [null, 'parent-null', 'parent-null', 'createparent-nullnull'], - [$modelId, 'parent', 'parent', 'createparent'. $modelId->getSerialized()] + [$modelId, 'parent', 'parent', 'createparent' . $modelId->getSerialized()] ]; } diff --git a/tests/Contao/Callback/AbstractContainerCallbackListenerTest.php b/tests/Contao/Callback/AbstractContainerCallbackListenerTest.php index ce24ed58..cbca7cd9 100644 --- a/tests/Contao/Callback/AbstractContainerCallbackListenerTest.php +++ b/tests/Contao/Callback/AbstractContainerCallbackListenerTest.php @@ -94,7 +94,7 @@ class AbstractContainerCallbackListenerTest extends TestCase /** @SuppressWarnings(PHPMD.UnusedFormalParameter) - phpmd can not handle the use syntax. */ protected function getCallback($value) { - return function () use($value) { + return function () use ($value) { throw new \Exception('The callback should not be executed as it is only mocked'); }; } @@ -124,7 +124,6 @@ protected function mockContainerEvent($class, $tablename) ->willReturn($this->mockEnvironment($tablename)); } } else { - $event = $this ->getMockBuilder($class) ->setMethods(['unknownMethod']) diff --git a/tests/Contao/Callback/AbstractReturningPropertyCallbackListenerTest.php b/tests/Contao/Callback/AbstractReturningPropertyCallbackListenerTest.php index abe059f5..cfa7941f 100644 --- a/tests/Contao/Callback/AbstractReturningPropertyCallbackListenerTest.php +++ b/tests/Contao/Callback/AbstractReturningPropertyCallbackListenerTest.php @@ -65,7 +65,7 @@ class AbstractReturningPropertyCallbackListenerTest extends TestCase /** @SuppressWarnings(PHPMD.UnusedFormalParameter) - phpmd can not handle the use syntax. */ protected function getCallback($value) { - return function () use($value) { + return function () use ($value) { throw new \Exception('The callback should not be executed as it is only mocked'); }; } diff --git a/tests/Contao/Dca/Builder/Legacy/LegacyDcaDataDefinitionBuilderTest.php b/tests/Contao/Dca/Builder/Legacy/LegacyDcaDataDefinitionBuilderTest.php index edd32e9f..7b2ccb3b 100644 --- a/tests/Contao/Dca/Builder/Legacy/LegacyDcaDataDefinitionBuilderTest.php +++ b/tests/Contao/Dca/Builder/Legacy/LegacyDcaDataDefinitionBuilderTest.php @@ -29,6 +29,7 @@ use ContaoCommunityAlliance\DcGeneral\DefaultEnvironment; use ContaoCommunityAlliance\DcGeneral\Factory\Event\BuildDataDefinitionEvent; use ContaoCommunityAlliance\DcGeneral\Test\TestCase; +use PHPUnit\Framework\MockObject\MockObject; use Symfony\Component\EventDispatcher\EventDispatcher; /** @@ -53,7 +54,7 @@ class LegacyDcaDataDefinitionBuilderTest extends TestCase * @param string $eventName * @param Eventdispatcher $dispatcher * - * @return \PHPUnit_Framework_MockObject_MockObject|LegacyDcaDataDefinitionBuilder + * @return MockObject|LegacyDcaDataDefinitionBuilder */ public function mockBuilderWithDca($dca, $eventName, $dispatcher) { @@ -69,14 +70,15 @@ public function mockBuilderWithDca($dca, $eventName, $dispatcher) ->method('loadDca') ->will( self::returnCallback( - function () use ($mock, $dca, $class) { - $reflection = new \ReflectionProperty($class, 'dca'); - $reflection->setAccessible(true); - $reflection->setValue($mock, $dca); - - return true; - } - )); + function () use ($mock, $dca, $class) { + $reflection = new \ReflectionProperty($class, 'dca'); + $reflection->setAccessible(true); + $reflection->setValue($mock, $dca); + + return true; + } + ) + ); $reflection = new \ReflectionProperty($class, 'eventName'); $reflection->setAccessible(true); diff --git a/tests/Contao/View/Contao2BackendView/Subscriber/CheckPermissionTest.php b/tests/Contao/View/Contao2BackendView/Subscriber/CheckPermissionTest.php index e291ad43..ce28e156 100644 --- a/tests/Contao/View/Contao2BackendView/Subscriber/CheckPermissionTest.php +++ b/tests/Contao/View/Contao2BackendView/Subscriber/CheckPermissionTest.php @@ -75,7 +75,9 @@ public function testCheckPermissionForProperties() ); $propertyNotExist = new Property('property13'); $property21 = new Property('property21'); - $property21->setVisibleCondition($prop21chain = new PropertyConditionChain([], PropertyConditionChain::OR_CONJUNCTION)); + $property21->setVisibleCondition( + $prop21chain = new PropertyConditionChain([], PropertyConditionChain::OR_CONJUNCTION) + ); $property22 = new Property('property22'); $property22->setVisibleCondition($prop22chain = new PropertyConditionChain()); $palette1 = $this->getMockBuilder(PaletteInterface::class)->getMockForAbstractClass(); diff --git a/tests/Controller/ModelCollectorTest.php b/tests/Controller/ModelCollectorTest.php index d30b51b9..edf1e30f 100644 --- a/tests/Controller/ModelCollectorTest.php +++ b/tests/Controller/ModelCollectorTest.php @@ -140,8 +140,10 @@ public function testGetModel($modelId, $providerName) $parentProperties = $this->mockPropertiesDefinition(); $parentProperties->method('getPropertyNames')->willReturn(['test-parent-property']); $parentDataDefinition = $this->mockDefinitionContainer(); - $parentDataDefinition->method('getName')->willReturn(ModelId::fromSerialized( - \is_object($modelId) ? $modelId->getSerialized() : $modelId)->getDataProviderName()); + $parentDataDefinition->method('getName')->willReturn( + ModelId::fromSerialized(\is_object($modelId) ? $modelId->getSerialized() : $modelId) + ->getDataProviderName() + ); $parentDataDefinition->method('getPropertiesDefinition')->willReturn($parentProperties); $environment->method('getParentDataDefinition')->willReturn($parentDataDefinition); @@ -384,7 +386,7 @@ function (string $providerName): array { case 'grandparent': return [$this->createParentChildCondition('grandparent', 'parent')]; - case 'parent': + case 'parent': return [$this->createParentChildCondition('parent', 'child')]; default: diff --git a/tests/DataDefinition/ModelRelationship/ParentChildConditionTest.php b/tests/DataDefinition/ModelRelationship/ParentChildConditionTest.php index c58a6334..16c88bca 100644 --- a/tests/DataDefinition/ModelRelationship/ParentChildConditionTest.php +++ b/tests/DataDefinition/ModelRelationship/ParentChildConditionTest.php @@ -48,7 +48,8 @@ public function testMatchesForChildFromOtherProvider() $condition = new ParentChildCondition(); $condition - ->setFilterArray([ + ->setFilterArray( + [ [ 'local' => 'id', 'operation' => '=', @@ -79,7 +80,8 @@ public function testMatchesForParentFromOtherProvider() $condition = new ParentChildCondition(); $condition - ->setFilterArray([ + ->setFilterArray( + [ [ 'local' => 'id', 'operation' => '=', @@ -110,7 +112,8 @@ public function testMatchesForNoParentProvider() $condition = new ParentChildCondition(); $condition - ->setFilterArray([ + ->setFilterArray( + [ [ 'local' => 'id', 'operation' => '=', @@ -140,7 +143,8 @@ public function testMatchesForNoDestinationProvider() $condition = new ParentChildCondition(); $condition - ->setFilterArray([ + ->setFilterArray( + [ [ 'local' => 'id', 'operation' => '=', @@ -167,7 +171,8 @@ public function testMatches() $child->setPropertyRaw('pid', 1); $condition = new ParentChildCondition(); - $condition->setFilterArray([ + $condition->setFilterArray( + [ [ 'local' => 'id', 'operation' => '=', @@ -194,7 +199,8 @@ public function testMatchesRemoteValue() $child->setID(2); $condition = new ParentChildCondition(); - $condition->setFilterArray([ + $condition->setFilterArray( + [ [ 'local' => 'id', 'operation' => '=', diff --git a/tests/DataDefinition/ModelRelationship/RootConditionTest.php b/tests/DataDefinition/ModelRelationship/RootConditionTest.php index a799193b..2b884516 100644 --- a/tests/DataDefinition/ModelRelationship/RootConditionTest.php +++ b/tests/DataDefinition/ModelRelationship/RootConditionTest.php @@ -45,7 +45,8 @@ public function testMatchesForChildFromOtherProvider() $condition = new RootCondition(); $condition - ->setFilterArray([ + ->setFilterArray( + [ [ 'value' => '0', 'operation' => '=', @@ -72,7 +73,8 @@ public function testMatches() $condition = new RootCondition(); $condition - ->setFilterArray([ + ->setFilterArray( + [ [ 'value' => '0', 'operation' => '=', diff --git a/tests/DcGeneralTest.php b/tests/DcGeneralTest.php index edec1e76..4761be32 100644 --- a/tests/DcGeneralTest.php +++ b/tests/DcGeneralTest.php @@ -78,9 +78,12 @@ public function testInstantiation() ->method('get') ->willReturnCallback(function ($name) use ($eventDispatcher, $definitionContainer) { switch ($name) { - case 'event_dispatcher': return $eventDispatcher; - case 'cca.translator.contao_translator': return new StaticTranslator(); - case 'cca.dc-general.data-definition-container': return $definitionContainer; + case 'event_dispatcher': + return $eventDispatcher; + case 'cca.translator.contao_translator': + return new StaticTranslator(); + case 'cca.dc-general.data-definition-container': + return $definitionContainer; } return null; }); diff --git a/tests/Fixtures/Contao/Config.php b/tests/Fixtures/Contao/Config.php index 1363407f..406f9dbb 100644 --- a/tests/Fixtures/Contao/Config.php +++ b/tests/Fixtures/Contao/Config.php @@ -24,7 +24,6 @@ */ class Config { - /** * Object instance (Singleton) * @var \Config @@ -38,8 +37,7 @@ class Config */ public static function getInstance() { - if (static::$objInstance === null) - { + if (static::$objInstance === null) { static::$objInstance = new static(); } @@ -57,8 +55,7 @@ public static function getInstance() */ public static function get($strKey) { - if (isset($GLOBALS['TL_CONFIG'][$strKey])) - { + if (isset($GLOBALS['TL_CONFIG'][$strKey])) { return $GLOBALS['TL_CONFIG'][$strKey]; } diff --git a/tests/Fixtures/Contao/Template.php b/tests/Fixtures/Contao/Template.php index 699712b3..eb8f54dc 100644 --- a/tests/Fixtures/Contao/Template.php +++ b/tests/Fixtures/Contao/Template.php @@ -42,7 +42,7 @@ class Template * @param string $strTemplate The template name * @param string $strContentType The content type (defaults to "text/html") */ - public function __construct($strTemplate='', $strContentType='text/html') + public function __construct($strTemplate = '', $strContentType = 'text/html') { $this->strTemplate = $strTemplate; $this->strContentType = $strContentType; From 65bb03d0ebbb76a24af6b1a05236489d78061567 Mon Sep 17 00:00:00 2001 From: Christian Schiffler Date: Mon, 31 Oct 2022 18:08:35 +0100 Subject: [PATCH 12/53] Fix tests --- tests/Contao/Event/SubscriberTest.php | 2 +- tests/DcGeneralTest.php | 4 ++-- tests/Factory/DcGeneralFactoryTest.php | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/Contao/Event/SubscriberTest.php b/tests/Contao/Event/SubscriberTest.php index 2d656387..455f2d61 100644 --- a/tests/Contao/Event/SubscriberTest.php +++ b/tests/Contao/Event/SubscriberTest.php @@ -166,7 +166,7 @@ public function testResolveWidgetErrorMessage($error, $excepted) $scopeDeterminator = $this->mockScopeDeterminator(); $dispatcher->addListener($event::NAME, [new Subscriber($scopeDeterminator), 'resolveWidgetErrorMessage']); - $dispatcher->dispatch($event::NAME, $event); + $dispatcher->dispatch($event, $event::NAME); self::assertSame($excepted, $event->getError()); } diff --git a/tests/DcGeneralTest.php b/tests/DcGeneralTest.php index 4761be32..66fad16a 100644 --- a/tests/DcGeneralTest.php +++ b/tests/DcGeneralTest.php @@ -28,7 +28,7 @@ use ContaoCommunityAlliance\DcGeneral\Factory\Event\PopulateEnvironmentEvent; use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; use ContaoCommunityAlliance\Translator\StaticTranslator; -use Doctrine\Common\Cache\Cache; +use Symfony\Component\Cache\Adapter\ArrayAdapter; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\EventDispatcher\EventDispatcher; @@ -122,7 +122,7 @@ public function testInstantiation() 'palettes' => [] ]; - $cache = $this->getMockForAbstractClass(Cache::class); + $cache = new ArrayAdapter(); $dataContainerFoo = new \DC_General('tl_foo', [], $cache); diff --git a/tests/Factory/DcGeneralFactoryTest.php b/tests/Factory/DcGeneralFactoryTest.php index 433b9713..ce1bda13 100644 --- a/tests/Factory/DcGeneralFactoryTest.php +++ b/tests/Factory/DcGeneralFactoryTest.php @@ -26,7 +26,7 @@ use ContaoCommunityAlliance\DcGeneral\Factory\DcGeneralFactory; use ContaoCommunityAlliance\DcGeneral\Test\TestCase; use ContaoCommunityAlliance\Translator\TranslatorInterface; -use Doctrine\Common\Cache\Cache; +use Symfony\Component\Cache\Adapter\ArrayAdapter; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\EventDispatcher\EventDispatcher; @@ -62,7 +62,7 @@ public function testCreateDcGeneral() ->with('test-container') ->willReturn(false); - $cache = $this->createMock(Cache::class); + $cache = new ArrayAdapter(); /** @var TranslatorInterface $mockTranslator */ $factory = new DcGeneralFactory($cache); From 89eeb8d247d578a4f167ac1cc68bade65529aae1 Mon Sep 17 00:00:00 2001 From: Christian Schiffler Date: Mon, 31 Oct 2022 18:10:10 +0100 Subject: [PATCH 13/53] Fix caching in legacy class Parameter was not adjusted in 4b614e62. --- src/DC/General.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/DC/General.php b/src/DC/General.php index 2db23ec3..d91cacd6 100644 --- a/src/DC/General.php +++ b/src/DC/General.php @@ -40,8 +40,8 @@ use ContaoCommunityAlliance\DcGeneral\Factory\Event\PopulateEnvironmentEvent; use ContaoCommunityAlliance\DcGeneral\View\ViewInterface; use ContaoCommunityAlliance\Translator\TranslatorInterface; -use Doctrine\Common\Cache\Cache; use Symfony\Component\EventDispatcher\EventDispatcherInterface; +use Symfony\Contracts\Cache\CacheInterface; /** * This class is only present so Contao can instantiate a backend properly as it needs a \DataContainer descendant. @@ -62,15 +62,15 @@ class General extends DataContainer implements DataContainerInterface /** * Create a new instance. * - * @param string $tableName The table name. - * @param array $module The modules. - * @param Cache|null $cache The cache. + * @param string $tableName The table name. + * @param array $module The modules. + * @param CacheInterface|null $cache The cache. * * @SuppressWarnings(PHPMD.CamelCaseVariableName) * @SuppressWarnings(PHPMD.Superglobals) * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function __construct($tableName, array $module = [], Cache $cache = null) + public function __construct($tableName, array $module = [], CacheInterface $cache = null) { // Prevent "Recoverable error: Argument X passed to SomClass::someMethod() must be an instance of DataContainer, // instance of ContaoCommunityAlliance\DcGeneral\DC_General given" in callbacks. From 31273966abe000d0c927ea365cf7d3656a7e3561 Mon Sep 17 00:00:00 2001 From: Christian Schiffler Date: Mon, 31 Oct 2022 18:39:29 +0100 Subject: [PATCH 14/53] Fix composer-require-checker issues --- .composer-require-checker.json | 14 +++++++++--- composer.json | 22 ++++++++++++++----- .../Dca/Populator/HardCodedPopulator.php | 3 ++- 3 files changed, 30 insertions(+), 9 deletions(-) diff --git a/.composer-require-checker.json b/.composer-require-checker.json index 238bb3ab..448a6c93 100644 --- a/.composer-require-checker.json +++ b/.composer-require-checker.json @@ -1,6 +1,11 @@ { "symbol-whitelist": [ - "array", "bool", "false", "int", "null", "self", "static", "string", "true", "void", + "array", "bool", "false", "int", "null", "self", "static", "parent", "string", "true", "void", + "ampersand", "array_insert", "array_is_assoc", "nl2br_html5", "TL_ERROR", + "ContaoTwigInitializeEvent", + "ContaoCommunityAlliance\\DcGeneral\\Contao\\View\\Contao2BackendView\\Exception\\EditOnlyModeException", + "ContaoCommunityAlliance\\DcGeneral\\Contao\\View\\Contao2BackendView\\Exception\\NotCreatableException", + "ContaoCommunityAlliance\\DcGeneral\\Contao\\View\\Contao2BackendView\\Exception\\NotDeletableException", "ContaoCommunityAlliance\\Contao\\EventDispatcher\\CcaEventDispatcherBundle", "Contao\\Calendar", "Contao\\CalendarEventsModel", @@ -10,7 +15,10 @@ "Contao\\ManagerPlugin\\Bundle\\BundlePluginInterface", "Contao\\ManagerPlugin\\Bundle\\Config\\BundleConfig", "Contao\\ManagerPlugin\\Bundle\\Parser\\ParserInterface", + "Contao\\ManagerPlugin\\Routing\\RoutingPluginInterface", "Contao\\NewsArchiveModel", - "Contao\\NewsModel" + "Contao\\NewsModel", + "FOS\\HttpCache\\CacheInvalidator", + "MenAtWork\\MultiColumnWizardBundle\\Event\\GetOptionsEvent" ] -} \ No newline at end of file +} diff --git a/composer.json b/composer.json index 27a1cd6d..0e8dc4b1 100644 --- a/composer.json +++ b/composer.json @@ -39,13 +39,25 @@ "contao-community-alliance/translator": "^2.3", "contao-community-alliance/url-builder": "^1.3", "contao/core-bundle": "^4.13", + "contao/image": "^1.1", "doctrine/cache": "^1.13 || ^2.1", + "doctrine/dbal": "^3.5", + "knplabs/knp-menu": "^3.1", "psr/event-dispatcher": "^1.0", - "symfony/cache": "4.4.* || ^5.4", - "symfony/cache-contracts": "^1.1.7 || ^2", - "symfony/event-dispatcher": "4.4.* || ^5.4", - "symfony/expression-language": "4.4.* || ^5.4", - "symfony/polyfill-mbstring": "^1.0" + "symfony/cache": "^5.4", + "symfony/cache-contracts": "^2.5.2", + "symfony/config": "^5.4", + "symfony/dependency-injection": "^5.4", + "symfony/event-dispatcher": "^5.4", + "symfony/event-dispatcher-contracts": "^2.0 || ^3.0", + "symfony/expression-language": "^5.4", + "symfony/http-foundation": "^5.4", + "symfony/http-kernel": "^5.4", + "symfony/polyfill-mbstring": "^1.0", + "symfony/routing": "^5.4", + "symfony/security-core": "^5.4", + "symfony/translation-contracts": "^2.5", + "twig/twig": "^3.0" }, "require-dev": { "contao/manager-plugin": "^2.8", diff --git a/src/Contao/Dca/Populator/HardCodedPopulator.php b/src/Contao/Dca/Populator/HardCodedPopulator.php index fff23e39..13c5937f 100644 --- a/src/Contao/Dca/Populator/HardCodedPopulator.php +++ b/src/Contao/Dca/Populator/HardCodedPopulator.php @@ -21,6 +21,7 @@ namespace ContaoCommunityAlliance\DcGeneral\Contao\Dca\Populator; +use Contao\System; use ContaoCommunityAlliance\DcGeneral\BaseConfigRegistry; use ContaoCommunityAlliance\DcGeneral\Clipboard\Clipboard; use ContaoCommunityAlliance\DcGeneral\Contao\InputProvider; @@ -68,7 +69,7 @@ public function populateController(EnvironmentInterface $environment) public function populate(EnvironmentInterface $environment) { if (!$environment->getSessionStorage()) { - $sessionStorage = \System::getContainer()->get('cca.dc-general.session_factory')->createService(); + $sessionStorage = System::getContainer()->get('cca.dc-general.session_factory')->createService(); $sessionStorage->setScope('DC_GENERAL_' . \strtoupper($environment->getDataDefinition()->getName())); $environment->setSessionStorage($sessionStorage); // @codingStandardsIgnoreStart From b8afd7745a58a2ade67bc16dec499705ff65df68 Mon Sep 17 00:00:00 2001 From: Christian Schiffler Date: Mon, 31 Oct 2022 18:40:02 +0100 Subject: [PATCH 15/53] Swap non existent class BadQueryStringException with BadRequestHttpException --- src/Controller/BackendTreeController.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Controller/BackendTreeController.php b/src/Controller/BackendTreeController.php index e98d675c..4882e1b7 100644 --- a/src/Controller/BackendTreeController.php +++ b/src/Controller/BackendTreeController.php @@ -35,7 +35,6 @@ use ContaoCommunityAlliance\DcGeneral\Data\PropertyValueBag; use ContaoCommunityAlliance\DcGeneral\DcGeneral; use ContaoCommunityAlliance\DcGeneral\Factory\DcGeneralFactory; -use http\Exception\BadQueryStringException; use Symfony\Component\DependencyInjection\ContainerAwareInterface; use Symfony\Component\DependencyInjection\ContainerAwareTrait; use Symfony\Component\HttpFoundation\Request; @@ -240,7 +239,7 @@ private function runBackendTreeToggle(Request $request) * @return Response * * @throws BadRequestHttpException This request isn`t from type ajax. - * @throws BadQueryStringException No picker was given here. + * @throws BadRequestHttpException No picker was given here. * * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ @@ -251,7 +250,7 @@ private function runBackendTreeUpdate(Request $request) } if (null === ($request->query->get('picker'))) { - throw new BadQueryStringException('No picker was given here.'); + throw new BadRequestHttpException('No picker was given here.'); } $picker = $this->container->get('contao.picker.builder')->createFromData($request->query->get('picker')); From d7b4d023f2fe16f5b0a777622a8d917521c596c6 Mon Sep 17 00:00:00 2001 From: Christian Schiffler Date: Mon, 31 Oct 2022 18:43:22 +0100 Subject: [PATCH 16/53] Normalize composer.json --- composer.json | 200 +++++++++++++++++++++++++------------------------- 1 file changed, 100 insertions(+), 100 deletions(-) diff --git a/composer.json b/composer.json index 0e8dc4b1..4ba6bd1c 100644 --- a/composer.json +++ b/composer.json @@ -1,105 +1,105 @@ { - "name": "contao-community-alliance/dc-general", - "description": "Universal data container for Contao", - "license": "LGPL-3.0-or-later", - "type": "contao-bundle", - "keywords": [ - "framework", - "data", - "container", - "driver", - "php", - "contao" - ], - "authors": [ - { - "name": "Christian Schiffler", - "email": "c.schiffler@cyberspectrum.de", - "homepage": "https://www.cyberspectrum.de", - "role": "Developer" - }, - { - "name": "Stefan Heimes", - "email": "stefan_heimes@hotmail.com", - "role": "Developer" - } - ], - "homepage": "https://c-c-a.org/", - "support": { - "issues": "https://github.com/contao-community-alliance/dc-general/issues", - "wiki": "https://de.contaowiki.org/DC_General", - "irc": "irc://irc.freenode.org/contao.dev", - "source": "https://github.com/contao-community-alliance/dc-general" - }, - "require": { - "php": "^7.4 || ^8.0", - "ext-json": "*", - "ext-pdo": "*", - "contao-community-alliance/events-contao-bindings": "^4.13", - "contao-community-alliance/translator": "^2.3", - "contao-community-alliance/url-builder": "^1.3", - "contao/core-bundle": "^4.13", - "contao/image": "^1.1", - "doctrine/cache": "^1.13 || ^2.1", - "doctrine/dbal": "^3.5", - "knplabs/knp-menu": "^3.1", - "psr/event-dispatcher": "^1.0", - "symfony/cache": "^5.4", - "symfony/cache-contracts": "^2.5.2", - "symfony/config": "^5.4", - "symfony/dependency-injection": "^5.4", - "symfony/event-dispatcher": "^5.4", - "symfony/event-dispatcher-contracts": "^2.0 || ^3.0", - "symfony/expression-language": "^5.4", - "symfony/http-foundation": "^5.4", - "symfony/http-kernel": "^5.4", - "symfony/polyfill-mbstring": "^1.0", - "symfony/routing": "^5.4", - "symfony/security-core": "^5.4", - "symfony/translation-contracts": "^2.5", - "twig/twig": "^3.0" - }, - "require-dev": { - "contao/manager-plugin": "^2.8", - "friendsofsymfony/http-cache": "^2.9", - "menatwork/contao-multicolumnwizard-bundle": "^3.4", - "php-http/guzzle6-adapter": "^2.0", - "phpcq/runner-bootstrap": "^1.0@dev" - }, - "conflict": { - "menatwork/contao-multicolumnwizard-bundle": "<3.4.9" - }, - "autoload": { - "psr-4": { - "ContaoCommunityAlliance\\DcGeneral\\": "src/" - }, - "classmap": [ - "contao-compat/DC_General.php" - ], - "files": [ - "src/deprecated-autoload.php" - ] + "name": "contao-community-alliance/dc-general", + "description": "Universal data container for Contao", + "license": "LGPL-3.0-or-later", + "type": "contao-bundle", + "keywords": [ + "framework", + "data", + "container", + "driver", + "php", + "contao" + ], + "authors": [ + { + "name": "Christian Schiffler", + "email": "c.schiffler@cyberspectrum.de", + "homepage": "https://www.cyberspectrum.de", + "role": "Developer" }, - "autoload-dev": { - "psr-4": { - "ContaoCommunityAlliance\\DcGeneral\\Test\\": "tests/" - } - }, - "config": { - "allow-plugins": { - "contao-components/installer": false, - "contao/manager-plugin": true - } - }, - "extra": { - "branch-alias": { - "dev-master": "2.2.x-dev", - "dev-release/2.3.0": "2.3.x-dev", - "dev-support/2.1.x": "2.1.x-dev" - }, - "contao-manager-plugin": "ContaoCommunityAlliance\\DcGeneral\\ContaoManager\\Plugin" + { + "name": "Stefan Heimes", + "email": "stefan_heimes@hotmail.com", + "role": "Developer" + } + ], + "homepage": "https://c-c-a.org/", + "support": { + "issues": "https://github.com/contao-community-alliance/dc-general/issues", + "wiki": "https://de.contaowiki.org/DC_General", + "irc": "irc://irc.freenode.org/contao.dev", + "source": "https://github.com/contao-community-alliance/dc-general" + }, + "require": { + "php": "^7.4 || ^8.0", + "ext-json": "*", + "ext-pdo": "*", + "contao-community-alliance/events-contao-bindings": "^4.13", + "contao-community-alliance/translator": "^2.3", + "contao-community-alliance/url-builder": "^1.3", + "contao/core-bundle": "^4.13", + "contao/image": "^1.1", + "doctrine/cache": "^1.13 || ^2.1", + "doctrine/dbal": "^3.5", + "knplabs/knp-menu": "^3.1", + "psr/event-dispatcher": "^1.0", + "symfony/cache": "^5.4", + "symfony/cache-contracts": "^2.5.2", + "symfony/config": "^5.4", + "symfony/dependency-injection": "^5.4", + "symfony/event-dispatcher": "^5.4", + "symfony/event-dispatcher-contracts": "^2.0 || ^3.0", + "symfony/expression-language": "^5.4", + "symfony/http-foundation": "^5.4", + "symfony/http-kernel": "^5.4", + "symfony/polyfill-mbstring": "^1.0", + "symfony/routing": "^5.4", + "symfony/security-core": "^5.4", + "symfony/translation-contracts": "^2.5", + "twig/twig": "^3.0" + }, + "require-dev": { + "contao/manager-plugin": "^2.8", + "friendsofsymfony/http-cache": "^2.9", + "menatwork/contao-multicolumnwizard-bundle": "^3.4", + "php-http/guzzle6-adapter": "^2.0", + "phpcq/runner-bootstrap": "^1.0@dev" + }, + "conflict": { + "menatwork/contao-multicolumnwizard-bundle": "<3.4.9" + }, + "autoload": { + "psr-4": { + "ContaoCommunityAlliance\\DcGeneral\\": "src/" }, - "scripts": { - "php-cs-fixer": "php-cs-fixer fix --rules=@PSR2" + "classmap": [ + "contao-compat/DC_General.php" + ], + "files": [ + "src/deprecated-autoload.php" + ] + }, + "autoload-dev": { + "psr-4": { + "ContaoCommunityAlliance\\DcGeneral\\Test\\": "tests/" + } + }, + "config": { + "allow-plugins": { + "contao-components/installer": false, + "contao/manager-plugin": true } + }, + "extra": { + "branch-alias": { + "dev-master": "2.2.x-dev", + "dev-release/2.3.0": "2.3.x-dev", + "dev-support/2.1.x": "2.1.x-dev" + }, + "contao-manager-plugin": "ContaoCommunityAlliance\\DcGeneral\\ContaoManager\\Plugin" + }, + "scripts": { + "php-cs-fixer": "php-cs-fixer fix --rules=@PSR2" + } } From c8b56485b405c626f4e861a0903c4c89eb13c401 Mon Sep 17 00:00:00 2001 From: Christian Schiffler Date: Mon, 31 Oct 2022 18:55:50 +0100 Subject: [PATCH 17/53] Remove obsolete autoload-validation-hack.php --- autoload-validation-hack.php | 46 ------------------------------------ 1 file changed, 46 deletions(-) delete mode 100644 autoload-validation-hack.php diff --git a/autoload-validation-hack.php b/autoload-validation-hack.php deleted file mode 100644 index 859eb4b1..00000000 --- a/autoload-validation-hack.php +++ /dev/null @@ -1,46 +0,0 @@ - - * @copyright 2013-2015 Contao Community Alliance. - * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0 - * @filesource - */ - -use PhpCodeQuality\AutoloadValidation\ClassLoader\EnumeratingClassLoader; -use PhpCodeQuality\AutoloadValidation\Exception\ParentClassNotFoundException; - -// This is the hack to mimic the Contao auto loader. -spl_autoload_register( - function ($class) { - if (in_array($class, ['listable', 'editable', 'executable', 'uploadable'])) { - $reflection = new ReflectionClass(\Contao\CoreBundle\ContaoCoreBundle::class); - require_once dirname($reflection->getFileName()) . '/Resources/contao/helper/interface.php'; - return true; - } - if (substr($class, 0, 7) === 'Contao\\') { - return null; - } - try { - spl_autoload_call('Contao\\' . $class); - } catch (ParentClassNotFoundException $exception) { - return null; - } - if (EnumeratingClassLoader::isLoaded('Contao\\' . $class) && !EnumeratingClassLoader::isLoaded($class)) { - class_alias('Contao\\' . $class, $class); - return true; - } - - return null; - } -); From da95c6b4aceb84675527b6362fbeacff2e95aa05 Mon Sep 17 00:00:00 2001 From: Christian Schiffler Date: Mon, 31 Oct 2022 19:01:00 +0100 Subject: [PATCH 18/53] Add psalm stubs for deprecated autoloading --- exception-stubs.phpstub | 12 ++++++++++++ psalm.xml | 3 +++ 2 files changed, 15 insertions(+) create mode 100644 exception-stubs.phpstub diff --git a/exception-stubs.phpstub b/exception-stubs.phpstub new file mode 100644 index 00000000..49502481 --- /dev/null +++ b/exception-stubs.phpstub @@ -0,0 +1,12 @@ + + + + From 39439133123da075805437b514ff6a7b26675d06 Mon Sep 17 00:00:00 2001 From: Christian Schiffler Date: Mon, 31 Oct 2022 19:06:34 +0100 Subject: [PATCH 19/53] Add missing null as return types in Environment The values are always null unless setter is called first. --- src/DefaultEnvironment.php | 48 ++++++++++++++++++------------------ src/EnvironmentInterface.php | 24 +++++++++--------- 2 files changed, 36 insertions(+), 36 deletions(-) diff --git a/src/DefaultEnvironment.php b/src/DefaultEnvironment.php index 7a4d8c8b..b8e133ae 100644 --- a/src/DefaultEnvironment.php +++ b/src/DefaultEnvironment.php @@ -40,86 +40,86 @@ class DefaultEnvironment implements EnvironmentInterface /** * The controller. * - * @var ControllerInterface + * @var ControllerInterface|null */ - protected $objController; + protected $objController = null; /** * The view in use. * - * @var ViewInterface + * @var ViewInterface|null */ - protected $objView; + protected $objView = null; /** * The data container definition. * - * @var ContainerInterface + * @var ContainerInterface|null */ - protected $objDataDefinition; + protected $objDataDefinition = null; /** * The data container definition of the parent table. * - * @var ContainerInterface + * @var ContainerInterface|null */ - protected $objParentDataDefinition; + protected $objParentDataDefinition = null; /** * The data container definition of the root table. * - * @var ContainerInterface + * @var ContainerInterface|null */ - protected $objRootDataDefinition; + protected $objRootDataDefinition = null; /** * The session storage. * - * @var SessionStorageInterface + * @var SessionStorageInterface|null */ - protected $sessionStorage; + protected $sessionStorage = null; /** * The attached input provider. * - * @var InputProviderInterface + * @var InputProviderInterface|null */ - protected $objInputProvider; + protected $objInputProvider = null; /** * The attached base config registry. * - * @var BaseConfigRegistryInterface + * @var BaseConfigRegistryInterface|null */ - protected $baseConfigRegistry; + protected $baseConfigRegistry = null; /** * The registered data providers. * - * @var DataProviderInterface[] + * @var array */ - protected $arrDataProvider; + protected $arrDataProvider = []; /** * The clipboard in use. * - * @var ClipboardInterface + * @var ClipboardInterface|null */ - protected $objClipboard; + protected $objClipboard = null; /** * The translator in use. * - * @var TranslatorInterface + * @var TranslatorInterface|null */ - protected $translator; + protected $translator = null; /** * The event propagator in use. * - * @var EventDispatcherInterface + * @var EventDispatcherInterface|null */ - protected $eventDispatcher; + protected $eventDispatcher = null; /** * {@inheritdoc} diff --git a/src/EnvironmentInterface.php b/src/EnvironmentInterface.php index bac35216..32d0e9fd 100644 --- a/src/EnvironmentInterface.php +++ b/src/EnvironmentInterface.php @@ -50,7 +50,7 @@ public function setController($objController); /** * Retrieve the Controller from the current setup. * - * @return ControllerInterface + * @return ControllerInterface|null */ public function getController(); @@ -66,7 +66,7 @@ public function setView($objView); /** * Retrieve the View from the current setup. * - * @return ViewInterface + * @return ViewInterface|null */ public function getView(); @@ -82,7 +82,7 @@ public function setDataDefinition($objContainer); /** * Retrieve the data definition for this instance. * - * @return ContainerInterface + * @return ContainerInterface|null */ public function getDataDefinition(); @@ -98,7 +98,7 @@ public function setParentDataDefinition($objContainer); /** * Retrieve the data definition for the parent container. This applies only when in parented mode. * - * @return ContainerInterface + * @return ContainerInterface|null */ public function getParentDataDefinition(); @@ -114,7 +114,7 @@ public function setRootDataDefinition($objContainer); /** * Retrieve the data definition for the root container. This applies only when in hierarchical mode. * - * @return ContainerInterface + * @return ContainerInterface|null */ public function getRootDataDefinition(); @@ -130,7 +130,7 @@ public function setSessionStorage(SessionStorageInterface $sessionStorage); /** * Retrieve the session storage. * - * @return SessionStorageInterface + * @return SessionStorageInterface|null */ public function getSessionStorage(); @@ -146,7 +146,7 @@ public function setInputProvider($objInputProvider); /** * Retrieve the input provider. * - * @return InputProviderInterface + * @return InputProviderInterface|null */ public function getInputProvider(); @@ -162,7 +162,7 @@ public function setBaseConfigRegistry($baseConfigRegistry); /** * Retrieve the base config registry. * - * @return BaseConfigRegistryInterface + * @return BaseConfigRegistryInterface|null */ public function getBaseConfigRegistry(); @@ -183,7 +183,7 @@ public function hasDataProvider($strSource = null); * * @param string|null $strSource The name of the source. * - * @return DataProviderInterface + * @return DataProviderInterface|null */ public function getDataProvider($strSource = null); @@ -209,7 +209,7 @@ public function removeDataProvider($strSource); /** * Return the clipboard. * - * @return ClipboardInterface + * @return ClipboardInterface|null */ public function getClipboard(); @@ -234,7 +234,7 @@ public function setTranslator(TranslatorInterface $manager); /** * Retrieve the translation manager to use. * - * @return TranslatorInterface + * @return TranslatorInterface|null */ public function getTranslator(); @@ -250,7 +250,7 @@ public function setEventDispatcher($dispatcher); /** * Get the event dispatcher to use. * - * @return EventDispatcherInterface + * @return EventDispatcherInterface|null */ public function getEventDispatcher(); } From 06b9af29541800bf5be3247199f230099e1bc96a Mon Sep 17 00:00:00 2001 From: Christian Schiffler Date: Mon, 31 Oct 2022 19:07:13 +0100 Subject: [PATCH 20/53] Fix some psalm issues --- src/Event/ActionEvent.php | 4 ++-- src/View/ActionHandler/CallActionTrait.php | 6 ++++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/Event/ActionEvent.php b/src/Event/ActionEvent.php index f9c94e49..d4bb5528 100644 --- a/src/Event/ActionEvent.php +++ b/src/Event/ActionEvent.php @@ -29,7 +29,7 @@ class ActionEvent extends AbstractActionAwareEvent /** * The action response, if any is set. * - * @var string + * @var string|null */ protected $response; @@ -49,7 +49,7 @@ public function setResponse($response) /** * Return the action response. * - * @return string + * @return string|null */ public function getResponse() { diff --git a/src/View/ActionHandler/CallActionTrait.php b/src/View/ActionHandler/CallActionTrait.php index 6e4c76b2..ba723a65 100644 --- a/src/View/ActionHandler/CallActionTrait.php +++ b/src/View/ActionHandler/CallActionTrait.php @@ -39,12 +39,14 @@ trait CallActionTrait * @param string $actionName The action name. * @param array $arguments The optional action arguments. * - * @return string + * @return string|null */ protected function callAction(EnvironmentInterface $environment, $actionName, $arguments = []) { $event = new ActionEvent($environment, new Action($actionName, $arguments)); - $environment->getEventDispatcher()->dispatch($event, DcGeneralEvents::ACTION); + if (null !== $dispatcher = $environment->getEventDispatcher()) { + $dispatcher->dispatch($event, DcGeneralEvents::ACTION); + } return $event->getResponse(); } From d60c87ff9d6833cca4ff806c07402ec83a8e0ce7 Mon Sep 17 00:00:00 2001 From: Sven Baumann Date: Sat, 14 Jan 2023 14:31:21 +0100 Subject: [PATCH 21/53] Added more php tests to the diagnostic github action --- .github/workflows/diagnostics.yml | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/.github/workflows/diagnostics.yml b/.github/workflows/diagnostics.yml index a03130e1..d2d12e2c 100644 --- a/.github/workflows/diagnostics.yml +++ b/.github/workflows/diagnostics.yml @@ -12,19 +12,10 @@ jobs: strategy: fail-fast: false matrix: - include: - - php: '7.4' - output: '-o github-action -o default' - phpcq_install: 'install' - contao: '~4.13.0' - - php: '8.0' - output: '-o default' - phpcq_install: 'update' - contao: '~4.13.0' - - php: '8.1' - output: '-o default' - phpcq_install: 'update' - contao: '~4.13.0' + php: [ '7.4', '8.0', '8.1', '8.2' ] + contao: [ '~4.13.0' ] + phpcq_install: [ 'update' ] + output: [ '-o github-action -o default' ] steps: - name: Pull source @@ -81,4 +72,4 @@ jobs: if: ${{ success() }} || ${{ failure() }} with: name: phpcq-builds-php-${{ matrix.php }}-${{ matrix.contao }} - path: .phpcq/build/ \ No newline at end of file + path: .phpcq/build/ From 2e6401b3a612637010373bf6b5ef530493a1ff51 Mon Sep 17 00:00:00 2001 From: Sven Baumann Date: Sat, 14 Jan 2023 14:32:21 +0100 Subject: [PATCH 22/53] Update the github action version who has deprecation warnings --- .github/workflows/diagnostics.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/diagnostics.yml b/.github/workflows/diagnostics.yml index d2d12e2c..f6c755d9 100644 --- a/.github/workflows/diagnostics.yml +++ b/.github/workflows/diagnostics.yml @@ -19,7 +19,7 @@ jobs: steps: - name: Pull source - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Setup PHP with PECL extension uses: shivammathur/setup-php@v2 @@ -28,7 +28,7 @@ jobs: # setup caches - name: Cache composer cache directory - uses: actions/cache@v1 + uses: actions/cache@v3 env: cache-name: composer-cache-dir with: @@ -36,7 +36,7 @@ jobs: key: ${{ runner.os }}-${{ matrix.php }}-build-${{ env.cache-name }} - name: Cache vendor directory - uses: actions/cache@v1 + uses: actions/cache@v3 env: cache-name: vendor with: @@ -46,7 +46,7 @@ jobs: ${{ runner.os }}-${{ matrix.php }}-${{ matrix.contao }}-build-${{ env.cache-name }}- - name: Cache phpcq directory - uses: actions/cache@v1 + uses: actions/cache@v3 env: cache-name: phpcq with: @@ -68,7 +68,7 @@ jobs: run: ./vendor/bin/phpcq run -v ${{ matrix.output }} - name: Upload build directory to artifact - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 if: ${{ success() }} || ${{ failure() }} with: name: phpcq-builds-php-${{ matrix.php }}-${{ matrix.contao }} From 5678e64a7d815fcd90d2e719e9b0989651e46800 Mon Sep 17 00:00:00 2001 From: Christian Schiffler Date: Tue, 17 Jan 2023 13:03:20 +0100 Subject: [PATCH 23/53] Fix psalm issues and CS in BaseConfigRegistry --- src/BaseConfigRegistry.php | 55 ++++++++++++++++++++++---------- tests/BaseConfigRegistryTest.php | 13 ++++++-- 2 files changed, 49 insertions(+), 19 deletions(-) diff --git a/src/BaseConfigRegistry.php b/src/BaseConfigRegistry.php index d9631a03..71656c76 100644 --- a/src/BaseConfigRegistry.php +++ b/src/BaseConfigRegistry.php @@ -26,6 +26,9 @@ use ContaoCommunityAlliance\DcGeneral\Data\ModelIdInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\BasicDefinitionInterface; use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralRuntimeException; +use LogicException; + +use function array_merge; /** * Registry for default data provider configurations to only resolve them once. @@ -35,16 +38,16 @@ class BaseConfigRegistry implements BaseConfigRegistryInterface /** * The attached environment. * - * @var EnvironmentInterface + * @var EnvironmentInterface|null */ - private $environment; + private ?EnvironmentInterface $environment = null; /** * The cached configurations. * - * @var ConfigInterface[] + * @var array */ - private $configs; + private array $configs = []; /** * Set the environment to use. @@ -65,6 +68,10 @@ public function setEnvironment(EnvironmentInterface $environment) */ public function getEnvironment() { + if (null === $this->environment) { + throw new LogicException('Property environment not initialized'); + } + return $this->environment; } @@ -78,18 +85,22 @@ public function getEnvironment() * * @throws DcGeneralRuntimeException When the parent item is not found. */ - private function addParentFilter($idParent, $config) + private function addParentFilter(ModelIdInterface $idParent, ConfigInterface $config): ConfigInterface { - $environment = $this->getEnvironment(); - $definition = $environment->getDataDefinition(); - $providerName = $definition->getBasicDefinition()->getDataProvider(); + $environment = $this->getEnvironment(); + $definition = $environment->getDataDefinition(); + if (null === $definition) { + throw new DcGeneralRuntimeException('Data definition not set.'); + } + $basicDefinition = $definition->getBasicDefinition(); + $providerName = $basicDefinition->getDataProvider(); $parentProviderName = $idParent->getDataProviderName(); $parentProvider = $environment->getDataProvider($parentProviderName); - if ($definition->getBasicDefinition()->getParentDataProvider() !== $parentProviderName) { + if ($basicDefinition->getParentDataProvider() !== $parentProviderName) { throw new DcGeneralRuntimeException( 'Unexpected parent provider ' . $parentProviderName . - ' (expected ' . $definition->getBasicDefinition()->getParentDataProvider() . ')' + ' (expected ' . $basicDefinition->getParentDataProvider() . ')' ); } @@ -111,7 +122,7 @@ private function addParentFilter($idParent, $config) $filter = $condition->getFilter($parent); if ($baseFilter) { - $filter = \array_merge($baseFilter, $filter); + $filter = array_merge($baseFilter, $filter); } $config->setFilter([['operation' => 'AND', 'children' => $filter]]); @@ -130,12 +141,19 @@ private function addParentFilter($idParent, $config) * * @return ConfigInterface */ - private function buildBaseConfig($parentId) + private function buildBaseConfig(?ModelIdInterface $parentId): ConfigInterface { $environment = $this->getEnvironment(); - $config = $environment->getDataProvider()->getEmptyConfig(); + $provider = $environment->getDataProvider(); + if (null === $provider) { + throw new DcGeneralRuntimeException('Data provider not set.'); + } + $config = $provider->getEmptyConfig(); $definition = $environment->getDataDefinition(); - $additional = $definition->getBasicDefinition()->getAdditionalFilter(); + if (null === $definition) { + throw new DcGeneralRuntimeException('Data definition not set.'); + } + $additional = $definition->getBasicDefinition()->getAdditionalFilter(); // Custom filter common for all modes. if ($additional) { @@ -145,6 +163,7 @@ private function buildBaseConfig($parentId) if (!$config->getSorting()) { /** @var Contao2BackendViewDefinitionInterface $viewDefinition */ $viewDefinition = $definition->getDefinition(Contao2BackendViewDefinitionInterface::NAME); + /** @psalm-suppress DeprecatedMethod - we can not change this in 2.x */ $config->setSorting($viewDefinition->getListingConfig()->getDefaultSortingFields()); } @@ -152,7 +171,11 @@ private function buildBaseConfig($parentId) if ($parentId) { $this->addParentFilter($parentId, $config); } elseif (BasicDefinitionInterface::MODE_PARENTEDLIST === $definition->getBasicDefinition()->getMode()) { - $pid = $environment->getInputProvider()->getParameter('pid'); + $input = $environment->getInputProvider(); + if (null === $input) { + throw new DcGeneralRuntimeException('Input provider not set.'); + } + $pid = $input->getParameter('pid'); $pidDetails = ModelId::fromSerialized($pid); $this->addParentFilter($pidDetails, $config); @@ -172,7 +195,7 @@ private function buildBaseConfig($parentId) */ public function getBaseConfig(ModelIdInterface $parentId = null) { - $key = $parentId ? $parentId->getSerialized() : null; + $key = $parentId ? $parentId->getSerialized() : ''; if (!isset($this->configs[$key])) { $this->configs[$key] = $this->buildBaseConfig($parentId); diff --git a/tests/BaseConfigRegistryTest.php b/tests/BaseConfigRegistryTest.php index 88ea35ad..6510ab83 100644 --- a/tests/BaseConfigRegistryTest.php +++ b/tests/BaseConfigRegistryTest.php @@ -54,13 +54,20 @@ public function testSetterAndGetter() $configRegistry = new BaseConfigRegistry(); - self::assertNull($configRegistry->getEnvironment()); - self::assertInstanceOf(BaseConfigRegistryInterface::class, $configRegistry->setEnvironment($environment)); self::assertInstanceOf(EnvironmentInterface::class, $configRegistry->getEnvironment()); self::assertSame($environment, $configRegistry->getEnvironment()); } + public function testGetterThrowsWhenEnvironmentNotSet() + { + $configRegistry = new BaseConfigRegistry(); + + $this->expectException(\LogicException::class); + + $configRegistry->getEnvironment(); + } + /** * @SuppressWarnings(PHPMD.ExcessiveMethodLength) * @SuppressWarnings(PHPMD.LongVariable) @@ -73,7 +80,7 @@ public function testGetBaseConfig() $dataDefinition = $this->getMockBuilder(DefaultContainer::class)->disableOriginalConstructor()->getMock(); $environment = - $this->getMockBuilder(DefaultEnvironment::class)->setMethods(['getDataDefinition'])->getMock(); + $this->getMockBuilder(DefaultEnvironment::class)->onlyMethods(['getDataDefinition'])->getMock(); $viewDefinition = $this->createMock(Contao2BackendViewDefinitionInterface::class); $listingConfig = $this->getMockBuilder(ListingConfigInterface::class)->getMock(); $modelRelationShip = $this->createMock(ModelRelationshipDefinitionInterface::class); From 6f8a441631e60378f65b45d047891bac7194f87f Mon Sep 17 00:00:00 2001 From: Christian Schiffler Date: Tue, 17 Jan 2023 13:03:47 +0100 Subject: [PATCH 24/53] Fix return type, fetch may return null --- src/Data/DataProviderInterface.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Data/DataProviderInterface.php b/src/Data/DataProviderInterface.php index f1007f8e..f402dc37 100644 --- a/src/Data/DataProviderInterface.php +++ b/src/Data/DataProviderInterface.php @@ -74,7 +74,7 @@ public function getEmptyCollection(); * * @param ConfigInterface $config The configuration to use. * - * @return ModelInterface + * @return null|ModelInterface */ public function fetch(ConfigInterface $config); From b415f22c04183b8edde514193370b91656406a04 Mon Sep 17 00:00:00 2001 From: Christian Schiffler Date: Tue, 17 Jan 2023 13:09:28 +0100 Subject: [PATCH 25/53] Fix psalm issues in InvalidateCacheTags --- src/Cache/Http/InvalidateCacheTags.php | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/src/Cache/Http/InvalidateCacheTags.php b/src/Cache/Http/InvalidateCacheTags.php index 3232dc0c..d6b88612 100644 --- a/src/Cache/Http/InvalidateCacheTags.php +++ b/src/Cache/Http/InvalidateCacheTags.php @@ -28,6 +28,7 @@ use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\BasicDefinitionInterface; use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; use ContaoCommunityAlliance\DcGeneral\Event\InvalidHttpCacheTagsEvent; +use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralRuntimeException; use FOS\HttpCache\CacheInvalidator; use Psr\EventDispatcher\EventDispatcherInterface; @@ -41,28 +42,28 @@ class InvalidateCacheTags implements InvalidateCacheTagsInterface * * @var string */ - private $namespace; + private string $namespace; /** * The event dispatcher. * * @var EventDispatcherInterface */ - private $dispatcher; + private EventDispatcherInterface $dispatcher; /** * The http cache manager. * * @var CacheInvalidator|null */ - private $cacheManager; + private ?CacheInvalidator $cacheManager; /** * The cache tags. * * @var string[] */ - private $tags; + private array $tags = []; /** * The constructor. @@ -126,15 +127,23 @@ private function addParentModelTag(ModelInterface $model, EnvironmentInterface $ if ((null === $environment->getParentDataDefinition())) { return; } + $dataDefinition = $environment->getDataDefinition(); + if (null === $dataDefinition) { + throw new DcGeneralRuntimeException('Data definition not set.'); + } - $definition = $environment->getDataDefinition()->getBasicDefinition(); + $definition = $dataDefinition->getBasicDefinition(); $collector = new ModelCollector($environment); switch ($definition->getMode()) { case BasicDefinitionInterface::MODE_HIERARCHICAL: - $this->addModelTag($collector->searchParentFromHierarchical($model)); + if (null !== $parentModel = $collector->searchParentFromHierarchical($model)) { + $this->addModelTag($parentModel); + } return; case BasicDefinitionInterface::MODE_PARENTEDLIST: - $this->addModelTag($collector->searchParentOf($model)); + if (null !== $parentModel = $collector->searchParentOf($model)) { + $this->addModelTag($parentModel); + } return; default: } From 0f3a0a7ca1223a44a087f3b21684295d5416a797 Mon Sep 17 00:00:00 2001 From: Christian Schiffler Date: Tue, 17 Jan 2023 13:10:17 +0100 Subject: [PATCH 26/53] Add missing return type --- src/CcaDcGeneralBundle.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/CcaDcGeneralBundle.php b/src/CcaDcGeneralBundle.php index 24dd9e7e..f099c13b 100644 --- a/src/CcaDcGeneralBundle.php +++ b/src/CcaDcGeneralBundle.php @@ -32,7 +32,7 @@ class CcaDcGeneralBundle extends Bundle /** * {@inheritDoc} */ - public function build(ContainerBuilder $container) + public function build(ContainerBuilder $container): void { parent::build($container); From 216227380e0b30c6a67815e1e6f94a4803237d4e Mon Sep 17 00:00:00 2001 From: Christian Schiffler Date: Tue, 17 Jan 2023 13:34:09 +0100 Subject: [PATCH 27/53] Fix phpDoc to use interface and not concrete class --- src/Clipboard/ItemInterface.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Clipboard/ItemInterface.php b/src/Clipboard/ItemInterface.php index 81a1413f..9ca4a77e 100644 --- a/src/Clipboard/ItemInterface.php +++ b/src/Clipboard/ItemInterface.php @@ -22,7 +22,7 @@ namespace ContaoCommunityAlliance\DcGeneral\Clipboard; -use ContaoCommunityAlliance\DcGeneral\Data\ModelId; +use ContaoCommunityAlliance\DcGeneral\Data\ModelIdInterface; /** * Interface ItemInterface. @@ -93,14 +93,14 @@ public function isDeepCopy(); /** * Retrieve the id of the parent model from this item. * - * @return ModelId|null + * @return ModelIdInterface|null */ public function getParentId(); /** * Retrieve the id of the model from this item. * - * @return ModelId|null + * @return ModelIdInterface|null */ public function getModelId(); From 767e53659e9a94bd17037c48b69e94ab2f82b16f Mon Sep 17 00:00:00 2001 From: Christian Schiffler Date: Wed, 18 Jan 2023 08:58:14 +0100 Subject: [PATCH 28/53] Fix psalm issues in clipboard namespace --- src/Clipboard/AbstractItem.php | 28 +++++---- src/Clipboard/Clipboard.php | 29 +++++++--- src/Clipboard/ClipboardInterface.php | 14 ++--- src/Clipboard/Filter.php | 85 +++++++++++++++------------- src/Clipboard/Item.php | 17 +++--- src/Clipboard/UnsavedItem.php | 9 +-- tests/Clipboard/ItemTest.php | 16 +----- 7 files changed, 104 insertions(+), 94 deletions(-) diff --git a/src/Clipboard/AbstractItem.php b/src/Clipboard/AbstractItem.php index 0fa492bf..c2859550 100644 --- a/src/Clipboard/AbstractItem.php +++ b/src/Clipboard/AbstractItem.php @@ -34,14 +34,14 @@ abstract class AbstractItem implements ItemInterface * * @var string */ - private $action; + private string $action; /** * The id of the parent model. * - * @var ModelIdInterface + * @var ModelIdInterface|null */ - private $parentId; + private ?ModelIdInterface $parentId; /** * Create a new instance. @@ -51,7 +51,7 @@ abstract class AbstractItem implements ItemInterface * * @throws \InvalidArgumentException When the action is not one of create, cut, copy or deep copy. */ - public function __construct($action, $parentId) + public function __construct(string $action, ?ModelIdInterface $parentId) { if ( ItemInterface::CREATE !== $action @@ -64,7 +64,7 @@ public function __construct($action, $parentId) ); } - $this->action = (string) $action; + $this->action = $action; $this->parentId = $parentId; } @@ -133,28 +133,34 @@ public function equals(ItemInterface $item) if ($this->getAction() !== $item->getAction()) { return false; } + $thisParent = $this->getParentId(); + $thatParent = $item->getParentId(); // One has a parent ID, the other not. - if (($this->getParentId() && !$item->getParentId()) || (!$this->getParentId() && $item->getParentId())) { + if (($thisParent && !$thatParent) || (!$thisParent && $thatParent)) { return false; } // The parent IDs are not equal. - if ($this->getParentId() && !$this->getParentId()->equals($item->getParentId())) { + if ($thisParent && !$thisParent->equals($thatParent)) { return false; } + $thisModel = $this->getModelId(); + $thatModel = $item->getModelId(); + // One has a model ID, the other not. - if (($this->getModelId() && !$item->getModelId()) || (!$this->getModelId() && $item->getModelId())) { + if (($thisModel && !$thatModel) || (!$thisModel && $thatModel)) { return false; } // Both have no model id and the data provider is different. - if (!($this->getModelId() || $item->getModelId())) { + if ((null === $thisModel) && (null === $thatModel)) { return ($this->getDataProviderName() === $item->getDataProviderName()); } - + assert($thisModel instanceof ModelIdInterface); + assert($thatModel instanceof ModelIdInterface); // The model IDs are not equal - return $this->getModelId()->equals($item->getModelId()); + return $thisModel->equals($thatModel); } } diff --git a/src/Clipboard/Clipboard.php b/src/Clipboard/Clipboard.php index 43ce722d..f3d733c0 100644 --- a/src/Clipboard/Clipboard.php +++ b/src/Clipboard/Clipboard.php @@ -25,6 +25,11 @@ use ContaoCommunityAlliance\DcGeneral\Data\ModelIdInterface; +use function base64_decode; +use function base64_encode; +use function serialize; +use function unserialize; + /** * Class Clipboard. * @@ -37,26 +42,30 @@ class Clipboard implements ClipboardInterface /** * The item collection (indexed by clipboard ids). * - * @var ItemInterface[] + * @var array */ - private $items = []; + private array $items = []; /** * The item collection (indexed by model ids). * - * @var ItemInterface[] + * @var array> */ - private $itemsByModelId = []; + private array $itemsByModelId = []; /** * {@inheritDoc} */ public function loadFrom($environment) { - $data = $environment->getSessionStorage()->get('CLIPBOARD'); + if (null === $session = $environment->getSessionStorage()) { + return $this; + } + + $data = $session->get('CLIPBOARD'); if ($data) { - $this->items = \unserialize(\base64_decode($data), ['allowed_classes' => true]); + $this->items = unserialize(base64_decode($data), ['allowed_classes' => true]); foreach ($this->items as $item) { if ($modelId = $item->getModelId()) { $this->itemsByModelId[$modelId->getSerialized()][$item->getClipboardId()] = $item; @@ -72,8 +81,12 @@ public function loadFrom($environment) */ public function saveTo($environment) { - $data = \base64_encode(\serialize($this->items)); - $environment->getSessionStorage()->set('CLIPBOARD', $data); + if (null === $session = $environment->getSessionStorage()) { + return $this; + } + + $data = base64_encode(serialize($this->items)); + $session->set('CLIPBOARD', $data); return $this; } diff --git a/src/Clipboard/ClipboardInterface.php b/src/Clipboard/ClipboardInterface.php index d767b250..1e76d964 100644 --- a/src/Clipboard/ClipboardInterface.php +++ b/src/Clipboard/ClipboardInterface.php @@ -36,22 +36,22 @@ interface ClipboardInterface /** * Load the content of the clipboard from the input provider stored in the environment. * - * @param EnvironmentInterface $objEnvironment The environment where the input provider will retrieve the + * @param EnvironmentInterface $environment The environment where the input provider will retrieve the * values from. * * @return static */ - public function loadFrom($objEnvironment); + public function loadFrom($environment); /** * Save the content of the clipboard to the input provider stored in the environment. * - * @param EnvironmentInterface $objEnvironment The environment where the input provider will store the + * @param EnvironmentInterface $environment The environment where the input provider will store the * values to. * * @return static */ - public function saveTo($objEnvironment); + public function saveTo($environment); /** * Push an item to the clipboard. @@ -112,7 +112,7 @@ public function hasId(ModelIdInterface $modelId); /** * Get all items from the clipboard. * - * @param FilterInterface|null $filter An item filter. + * @param FilterInterface $filter An item filter. * * @return ItemInterface[] */ @@ -121,7 +121,7 @@ public function fetch(FilterInterface $filter); /** * Determine if the clipboard is empty. * - * @param FilterInterface|null $filter An item filter. + * @param FilterInterface $filter An item filter. * * @return bool */ @@ -130,7 +130,7 @@ public function isEmpty(FilterInterface $filter); /** * Determine if the clipboard is not empty. * - * @param FilterInterface|null $filter An item filter. + * @param FilterInterface $filter An item filter. * * @return bool */ diff --git a/src/Clipboard/Filter.php b/src/Clipboard/Filter.php index fe4e8de4..9a8a9acc 100644 --- a/src/Clipboard/Filter.php +++ b/src/Clipboard/Filter.php @@ -25,6 +25,10 @@ use ContaoCommunityAlliance\DcGeneral\Data\ModelId; use Symfony\Component\ExpressionLanguage\ExpressionLanguage; +use function count; +use function implode; +use function sprintf; + /** * Class Filter. * @@ -39,21 +43,21 @@ class Filter implements FilterInterface * * @var array */ - private $expression = []; + private array $expression = []; /** * The expression variables. * * @var array */ - private $variables = []; + private array $variables = []; /** * Pre-compiled expression. * - * @var string + * @var string|null */ - private $compiled; + private ?string $compiled = null; public const MODEL_IS_FROM_PROVIDER_EXPRESSION = <<<'EXPR' ( @@ -141,6 +145,7 @@ class Filter implements FilterInterface */ public static function create() { + /** @psalm-suppress UnsafeInstantiation - Can not make this final in a minor release. :/ */ return new static(); } @@ -186,8 +191,8 @@ private function modelIsFromProvider($conjunction, $modelProviderName) $this->expression[] = $conjunction; } - $index = \count($this->variables); - $this->expression[] = \sprintf(self::MODEL_IS_FROM_PROVIDER_EXPRESSION, $index); + $index = count($this->variables); + $this->expression[] = sprintf(self::MODEL_IS_FROM_PROVIDER_EXPRESSION, $index); $this->variables[] = $modelProviderName; $this->compiled = null; @@ -236,8 +241,8 @@ private function modelIsNotFromProvider($conjunction, $modelProviderName) $this->expression[] = $conjunction; } - $index = \count($this->variables); - $this->expression[] = \sprintf(self::MODEL_IS_NOT_FROM_PROVIDER_EXPRESSION, $index); + $index = count($this->variables); + $this->expression[] = sprintf(self::MODEL_IS_NOT_FROM_PROVIDER_EXPRESSION, $index); $this->variables[] = $modelProviderName; $this->compiled = null; @@ -286,8 +291,8 @@ private function modelIs($conjunction, ModelId $modelId) $this->expression[] = $conjunction; } - $index = \count($this->variables); - $this->expression[] = \sprintf(self::MODEL_IS_EXPRESSION, $index); + $index = count($this->variables); + $this->expression[] = sprintf(self::MODEL_IS_EXPRESSION, $index); $this->variables[] = $modelId; $this->compiled = null; @@ -336,8 +341,8 @@ private function modelIsNot($conjunction, ModelId $modelId) $this->expression[] = $conjunction; } - $index = \count($this->variables); - $this->expression[] = \sprintf(self::MODEL_IS_NOT_EXPRESSION, $index); + $index = count($this->variables); + $this->expression[] = sprintf(self::MODEL_IS_NOT_EXPRESSION, $index); $this->variables[] = $modelId; $this->compiled = null; @@ -386,8 +391,8 @@ private function parentIsFromProvider($conjunction, $parentProviderName) $this->expression[] = $conjunction; } - $index = \count($this->variables); - $this->expression[] = \sprintf(self::PARENT_IS_FROM_PROVIDER_EXPRESSION, $index); + $index = count($this->variables); + $this->expression[] = sprintf(self::PARENT_IS_FROM_PROVIDER_EXPRESSION, $index); $this->variables[] = $parentProviderName; $this->compiled = null; @@ -436,8 +441,8 @@ private function parentIsNotFromProvider($conjunction, $parentProviderName) $this->expression[] = $conjunction; } - $index = \count($this->variables); - $this->expression[] = \sprintf(self::PARENT_IS_NOT_FROM_PROVIDER_EXPRESSION, $index); + $index = count($this->variables); + $this->expression[] = sprintf(self::PARENT_IS_NOT_FROM_PROVIDER_EXPRESSION, $index); $this->variables[] = $parentProviderName; $this->compiled = null; @@ -529,8 +534,8 @@ private function parentIs($conjunction, ModelId $parentModelId) $this->expression[] = $conjunction; } - $index = \count($this->variables); - $this->expression[] = \sprintf(self::PARENT_IS_EXPRESSION, $index); + $index = count($this->variables); + $this->expression[] = sprintf(self::PARENT_IS_EXPRESSION, $index); $this->variables[] = $parentModelId; $this->compiled = null; @@ -581,11 +586,11 @@ private function parentIsIn($conjunction, array $parentModelIds) $expression = []; foreach ($parentModelIds as $parentModelId) { - $index = \count($this->variables); - $expression[] = \sprintf(self::PARENT_IS_EXPRESSION, $index); + $index = count($this->variables); + $expression[] = sprintf(self::PARENT_IS_EXPRESSION, $index); $this->variables[] = $parentModelId; } - $this->expression[] = '(' . \implode(' or ', $expression) . ')'; + $this->expression[] = '(' . implode(' or ', $expression) . ')'; $this->compiled = null; return $this; @@ -633,8 +638,8 @@ private function parentIsNot($conjunction, ModelId $parentModelId) $this->expression[] = $conjunction; } - $index = \count($this->variables); - $this->expression[] = \sprintf(self::PARENT_IS_NOT_EXPRESSION, $index); + $index = count($this->variables); + $this->expression[] = sprintf(self::PARENT_IS_NOT_EXPRESSION, $index); $this->variables[] = $parentModelId; $this->compiled = null; @@ -685,11 +690,11 @@ private function parentIsNotIn($conjunction, array $parentModelIds) $expression = []; foreach ($parentModelIds as $parentModelId) { - $index = \count($this->variables); - $expression[] = \sprintf(self::PARENT_IS_NOT_EXPRESSION, $index); + $index = count($this->variables); + $expression[] = sprintf(self::PARENT_IS_NOT_EXPRESSION, $index); $this->variables[] = $parentModelId; } - $this->expression[] = '(' . \implode(' and ', $expression) . ')'; + $this->expression[] = '(' . implode(' and ', $expression) . ')'; $this->compiled = null; return $this; @@ -737,8 +742,8 @@ private function actionIs($conjunction, $action) $this->expression[] = $conjunction; } - $index = \count($this->variables); - $this->expression[] = \sprintf(self::ACTION_IS_EXPRESSION, $index); + $index = count($this->variables); + $this->expression[] = sprintf(self::ACTION_IS_EXPRESSION, $index); $this->variables[] = $action; $this->compiled = null; @@ -789,11 +794,11 @@ private function actionIsIn($conjunction, array $actions) $expression = []; foreach ($actions as $action) { - $index = \count($this->variables); - $expression[] = \sprintf(self::ACTION_IS_EXPRESSION, $index); + $index = count($this->variables); + $expression[] = sprintf(self::ACTION_IS_EXPRESSION, $index); $this->variables[] = $action; } - $this->expression[] = '(' . \implode(' or ', $expression) . ')'; + $this->expression[] = '(' . implode(' or ', $expression) . ')'; $this->compiled = null; return $this; @@ -841,8 +846,8 @@ private function actionIsNot($conjunction, $action) $this->expression[] = $conjunction; } - $index = \count($this->variables); - $this->expression[] = \sprintf(self::ACTION_IS_NOT_EXPRESSION, $index); + $index = count($this->variables); + $this->expression[] = sprintf(self::ACTION_IS_NOT_EXPRESSION, $index); $this->variables[] = $action; $this->compiled = null; @@ -893,11 +898,11 @@ private function actionIsNotIn($conjunction, array $actions) $expression = []; foreach ($actions as $action) { - $index = \count($this->variables); - $expression[] = \sprintf(self::ACTION_IS_NOT_EXPRESSION, $index); + $index = count($this->variables); + $expression[] = sprintf(self::ACTION_IS_NOT_EXPRESSION, $index); $this->variables[] = $action; } - $this->expression[] = '(' . \implode(' and ', $expression) . ')'; + $this->expression[] = '(' . implode(' and ', $expression) . ')'; $this->compiled = null; return $this; @@ -945,8 +950,8 @@ private function sub($conjunction, FilterInterface $filter) $this->expression[] = $conjunction; } - $index = \count($this->variables); - $this->expression[] = \sprintf(self::SUB_FILTER, $index); + $index = count($this->variables); + $this->expression[] = sprintf(self::SUB_FILTER, $index); $this->variables[] = $filter; $this->compiled = null; @@ -964,7 +969,7 @@ public function accepts(ItemInterface $item) if (null === $this->compiled) { $language = new ExpressionLanguage(); $expression = $this->getExpression(); - $this->compiled = \sprintf( + $this->compiled = sprintf( 'return %s;', $language->compile( $expression, @@ -988,7 +993,7 @@ public function accepts(ItemInterface $item) */ public function getExpression() { - return $this->expression ? \implode(' ', $this->expression) : 'true'; + return $this->expression ? implode(' ', $this->expression) : 'true'; } /** diff --git a/src/Clipboard/Item.php b/src/Clipboard/Item.php index ea9853a7..7c774e25 100644 --- a/src/Clipboard/Item.php +++ b/src/Clipboard/Item.php @@ -23,6 +23,7 @@ namespace ContaoCommunityAlliance\DcGeneral\Clipboard; use ContaoCommunityAlliance\DcGeneral\Data\ModelIdInterface; +use InvalidArgumentException; /** * {@inheritdoc} @@ -32,27 +33,23 @@ class Item extends AbstractItem /** * The id of the model. * - * @var ModelIdInterface|null + * @var ModelIdInterface */ - private $modelId; + private ModelIdInterface $modelId; /** * Create a new instance. * * @param string $action The action being performed. * @param ModelIdInterface|null $parentId The id of the parent model (null for no parent). - * @param ModelIdInterface|null $modelId The id of the model the action covers (may be null for "create" only). + * @param ModelIdInterface $modelId The id of the model the action covers (maybe null for "create" only). * - * @throws \InvalidArgumentException When the action is not one of create, cut, copy or deep copy. + * @throws InvalidArgumentException When the action is not one of create, cut, copy or deep copy. */ - public function __construct($action, $parentId, $modelId) + public function __construct(string $action, ?ModelIdInterface $parentId, ModelIdInterface $modelId) { parent::__construct($action, $parentId); - if (!$modelId instanceof ModelIdInterface) { - throw new \InvalidArgumentException('Invalid $modelId given.'); - } - $this->modelId = $modelId; } @@ -78,7 +75,7 @@ public function getDataProviderName() public function getClipboardId() { return $this->getAction() . - ($this->modelId ? $this->modelId->getSerialized() : 'null') . + $this->modelId->getSerialized() . (($parentId = $this->getParentId()) ? $parentId->getSerialized() : 'null'); } } diff --git a/src/Clipboard/UnsavedItem.php b/src/Clipboard/UnsavedItem.php index 2099998b..0dfd7f1e 100644 --- a/src/Clipboard/UnsavedItem.php +++ b/src/Clipboard/UnsavedItem.php @@ -22,6 +22,7 @@ namespace ContaoCommunityAlliance\DcGeneral\Clipboard; use ContaoCommunityAlliance\DcGeneral\Data\ModelIdInterface; +use InvalidArgumentException; /** * UnsavedItem is designed for new items being created which does not have an id yet. @@ -35,7 +36,7 @@ class UnsavedItem extends AbstractItem * * @var string */ - private $providerName; + private string $providerName; /** * Create a new instance. @@ -44,14 +45,14 @@ class UnsavedItem extends AbstractItem * @param ModelIdInterface|null $parentId The id of the parent model (null for no parent). * @param string $providerName The provider name of the item being created. * - * @throws \InvalidArgumentException When the action is not create. + * @throws InvalidArgumentException When the action is not create. */ - public function __construct($action, $parentId, $providerName) + public function __construct(string $action, ?ModelIdInterface $parentId, string $providerName) { parent::__construct($action, $parentId); if ($action !== ItemInterface::CREATE) { - throw new \InvalidArgumentException('UnsavedItem is designed for create actions only.'); + throw new InvalidArgumentException('UnsavedItem is designed for create actions only.'); } $this->providerName = $providerName; diff --git a/tests/Clipboard/ItemTest.php b/tests/Clipboard/ItemTest.php index be87448e..ce4f87b2 100644 --- a/tests/Clipboard/ItemTest.php +++ b/tests/Clipboard/ItemTest.php @@ -36,24 +36,12 @@ class ItemTest extends TestCase { public const TEST_PROVIDER = 'dummy-provider'; - /** - * Test the the Item requires an valid model id. - * - * @return void - */ - public function testItemRequiresModelId() - { - $this->expectException('InvalidArgumentException'); - - new Item(ItemInterface::CREATE, null, null); - } - /** * Run the tests with a parent id. * * @param ModelIdInterface|null $parentId Optional parent id. */ - private function runAssertsWithParentId($parentId) + private function runAssertsWithParentId(?ModelIdInterface $parentId): void { $item = new MockedAbstractItem(ItemInterface::CREATE, $parentId, self::TEST_PROVIDER); $unsavedItem = new UnsavedItem(ItemInterface::CREATE, $parentId, self::TEST_PROVIDER); @@ -85,7 +73,7 @@ private function runAssertsWithParentId($parentId) * * @return void */ - public function testCompare() + public function testCompare(): void { // Test without a parent id. $this->runAssertsWithParentId(null); From 978cde0eee015be03426824d0da6818caf7eb5a8 Mon Sep 17 00:00:00 2001 From: Christian Schiffler Date: Wed, 18 Jan 2023 09:19:37 +0100 Subject: [PATCH 29/53] Fix issues in cache handling --- .../Http/AbstractInvalidateCacheTags.php | 8 +++---- .../Http/DeleteModelInvalidateCacheTags.php | 23 +++++++++++++------ 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/src/Contao/Cache/Http/AbstractInvalidateCacheTags.php b/src/Contao/Cache/Http/AbstractInvalidateCacheTags.php index 7557295f..e1e79175 100644 --- a/src/Contao/Cache/Http/AbstractInvalidateCacheTags.php +++ b/src/Contao/Cache/Http/AbstractInvalidateCacheTags.php @@ -36,16 +36,16 @@ abstract class AbstractInvalidateCacheTags /** * The invalid http cache tags service. * - * @var ContaoInvalidateCacheTags + * @var InvalidateCacheTagsInterface */ - private $service; + private InvalidateCacheTagsInterface $service; /** * The dc general factory. * - * @var DcGeneralFactoryService + * @var DcGeneralFactoryServiceInterface */ - protected $factory; + protected DcGeneralFactoryServiceInterface $factory; /** * The constructor. diff --git a/src/Contao/Cache/Http/DeleteModelInvalidateCacheTags.php b/src/Contao/Cache/Http/DeleteModelInvalidateCacheTags.php index 921dca0d..40457379 100644 --- a/src/Contao/Cache/Http/DeleteModelInvalidateCacheTags.php +++ b/src/Contao/Cache/Http/DeleteModelInvalidateCacheTags.php @@ -24,6 +24,7 @@ use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; use ContaoCommunityAlliance\DcGeneral\Event\AbstractModelAwareEvent; use ContaoCommunityAlliance\DcGeneral\Factory\DcGeneralFactory; +use LogicException; /** * The persist invalidate http cache tags, is for a model be deleted. @@ -35,18 +36,26 @@ final class DeleteModelInvalidateCacheTags extends AbstractInvalidateCacheTags */ protected function getEnvironment(AbstractModelAwareEvent $event): EnvironmentInterface { + $environment = $event->getEnvironment(); + $definition = $environment->getDataDefinition(); + $providerName = $event->getModel()->getProviderName(); if ( - $event->getEnvironment()->getDataDefinition()->getBasicDefinition()->getDataProvider() - === $event->getModel()->getProviderName() + $definition + && $definition->getBasicDefinition()->getDataProvider() === $providerName ) { - return $event->getEnvironment(); + return $environment; + } + if (null === $dispatcher = $environment->getEventDispatcher()) { + throw new LogicException('No event dispatcher given'); + } + if (null === $translator = $environment->getTranslator()) { + throw new LogicException('No event translator given'); } - return $this->factory ->createFactory() - ->setContainerName($event->getModel()->getProviderName()) - ->setEventDispatcher($event->getEnvironment()->getEventDispatcher()) - ->setTranslator($event->getEnvironment()->getTranslator()) + ->setContainerName($providerName) + ->setEventDispatcher($dispatcher) + ->setTranslator($translator) ->createDcGeneral() ->getEnvironment(); } From a3d24bd0d3836650acebbe4ec17d8514445cf080 Mon Sep 17 00:00:00 2001 From: Christian Schiffler Date: Wed, 18 Jan 2023 12:29:42 +0100 Subject: [PATCH 30/53] Fix psalm issues in callback handlers --- .../Callback/AbstractCallbackListener.php | 32 +++++++++---- .../AbstractReturningCallbackListener.php | 9 +++- ...tractReturningPropertyCallbackListener.php | 46 +++++++++++-------- src/Contao/Callback/Callbacks.php | 45 ++++++++++++------ ...ContainerGetBreadcrumbCallbackListener.php | 15 ++---- .../ContainerGlobalButtonCallbackListener.php | 28 ++++------- .../ContainerHeaderCallbackListener.php | 15 ++---- .../ContainerOnCopyCallbackListener.php | 8 ++-- .../ContainerOnCutCallbackListener.php | 8 ++-- .../ContainerOnDeleteCallbackListener.php | 8 ++-- .../ContainerOnLoadCallbackListener.php | 24 +++++----- .../ContainerOnSubmitCallbackListener.php | 8 ++-- .../ContainerPasteButtonCallbackListener.php | 26 +++++------ ...ntainerPasteRootButtonCallbackListener.php | 26 +++++------ .../ModelChildRecordCallbackListener.php | 15 ++---- .../Callback/ModelGroupCallbackListener.php | 15 ++---- .../Callback/ModelLabelCallbackListener.php | 23 ++++------ .../ModelOperationButtonCallbackListener.php | 45 +++++++++--------- .../Callback/ModelOptionsCallbackListener.php | 15 ++---- .../PropertyInputFieldCallbackListener.php | 17 ++----- ...rtyInputFieldGetWizardCallbackListener.php | 15 ++---- ...rtyInputFieldGetXLabelCallbackListener.php | 17 ++----- .../PropertyOnLoadCallbackListener.php | 15 ++---- .../PropertyOnSaveCallbackListener.php | 15 ++---- 24 files changed, 213 insertions(+), 277 deletions(-) diff --git a/src/Contao/Callback/AbstractCallbackListener.php b/src/Contao/Callback/AbstractCallbackListener.php index 0d8c0e83..bf48c9f2 100644 --- a/src/Contao/Callback/AbstractCallbackListener.php +++ b/src/Contao/Callback/AbstractCallbackListener.php @@ -22,12 +22,17 @@ namespace ContaoCommunityAlliance\DcGeneral\Contao\Callback; -use ContaoCommunityAlliance\DcGeneral\Event\AbstractEnvironmentAwareEvent; +use ContaoCommunityAlliance\DcGeneral\EnvironmentAwareInterface; +use Symfony\Contracts\EventDispatcher\Event; + +use function call_user_func_array; /** * Class AbstractCallbackListener. * * Abstract base class for a callback listener. + * + * @template TEvent of Event */ abstract class AbstractCallbackListener { @@ -51,12 +56,12 @@ abstract class AbstractCallbackListener * @param array|callable $callback The callback to call when invoked. * @param array|null $restrictions The restrictions for the callback. */ - public function __construct($callback = null, $restrictions = null) + public function __construct($callback, $restrictions = null) { $this->callback = $callback; if ($restrictions) { - \call_user_func_array([$this, 'setRestrictions'], $restrictions); + call_user_func_array([$this, 'setRestrictions'], $restrictions); } } @@ -67,7 +72,7 @@ public function __construct($callback = null, $restrictions = null) * * @return void */ - public function setRestrictions($dataContainerName = null) + public function setRestrictions(?string $dataContainerName = null) { $this->dataContainerName = $dataContainerName; } @@ -75,15 +80,22 @@ public function setRestrictions($dataContainerName = null) /** * Check the restrictions against the information within the event and determine if the callback shall be executed. * - * @param AbstractEnvironmentAwareEvent $event The Event for which the callback shall be invoked. + * @param TEvent $event The Event for which the callback shall be invoked. * * @return bool */ public function wantToExecute($event) { - return (empty($this->dataContainerName) - || ($this->dataContainerName === $event->getEnvironment()->getDataDefinition()->getName()) - ); + if (empty($this->dataContainerName)) { + return true; + } + if (!$event instanceof EnvironmentAwareInterface) { + throw new \LogicException('Event is not an instance of ' . EnvironmentAwareInterface::class); + } + if (null === $definition = $event->getEnvironment()->getDataDefinition()) { + return false; + } + return ($this->dataContainerName === $definition->getName()); } /** @@ -99,7 +111,7 @@ public function getCallback() /** * Retrieve the arguments for the callback. * - * @param \Symfony\Contracts\EventDispatcher\Event $event The event being emitted. + * @param TEvent $event The event being emitted. * * @return array */ @@ -108,7 +120,7 @@ abstract public function getArgs($event); /** * Invoke the callback. * - * @param AbstractEnvironmentAwareEvent $event The Event for which the callback shall be invoked. + * @param TEvent $event The Event for which the callback shall be invoked. * * @return void */ diff --git a/src/Contao/Callback/AbstractReturningCallbackListener.php b/src/Contao/Callback/AbstractReturningCallbackListener.php index ece06290..71ae1755 100644 --- a/src/Contao/Callback/AbstractReturningCallbackListener.php +++ b/src/Contao/Callback/AbstractReturningCallbackListener.php @@ -22,18 +22,23 @@ namespace ContaoCommunityAlliance\DcGeneral\Contao\Callback; +use ContaoCommunityAlliance\DcGeneral\Event\AbstractEnvironmentAwareEvent; + /** * Class AbstractReturningCallbackListener. * * Abstract base class for callbacks that are returning a value. + * + * @template TEvent of AbstractEnvironmentAwareEvent + * @extends AbstractCallbackListener */ abstract class AbstractReturningCallbackListener extends AbstractCallbackListener { /** * Update the values in the event with the value returned by the callback. * - * @param \Symfony\Contracts\EventDispatcher\Event $event The event being emitted. - * @param mixed $value The value returned by the callback. + * @param TEvent $event The event being emitted. + * @param mixed $value The value returned by the callback. * * @return void */ diff --git a/src/Contao/Callback/AbstractReturningPropertyCallbackListener.php b/src/Contao/Callback/AbstractReturningPropertyCallbackListener.php index a7b56642..08e1e5d8 100644 --- a/src/Contao/Callback/AbstractReturningPropertyCallbackListener.php +++ b/src/Contao/Callback/AbstractReturningPropertyCallbackListener.php @@ -20,17 +20,21 @@ namespace ContaoCommunityAlliance\DcGeneral\Contao\Callback; -use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\BuildWidgetEvent; -use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\DecodePropertyValueForWidgetEvent; -use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\EncodePropertyValueFromWidgetEvent; -use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\GetPropertyOptionsEvent; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\Properties\PropertyInterface; +use ContaoCommunityAlliance\DcGeneral\Event\AbstractEnvironmentAwareEvent; +use InvalidArgumentException; +use Symfony\Contracts\EventDispatcher\Event; + +use function method_exists; /** * Class AbstractReturningPropertyCallbackListener. * * Abstract base class for a callback listener. * + * @template TEvent of AbstractEnvironmentAwareEvent + * @extends AbstractReturningCallbackListener + * * @SuppressWarnings(PHPMD.LongClassName) */ abstract class AbstractReturningPropertyCallbackListener extends AbstractReturningCallbackListener @@ -50,32 +54,34 @@ abstract class AbstractReturningPropertyCallbackListener extends AbstractReturni * * @return void */ - public function setRestrictions($dataContainerName = null, $propertyName = null) + public function setRestrictions(?string $dataContainerName = null, ?string $propertyName = null) { parent::setRestrictions($dataContainerName); $this->propertyName = $propertyName; } - // @codingStandardsIgnoreStart /** - * Check the restrictions against the information within the event and determine if the callback shall be executed. - * - * @param BuildWidgetEvent|DecodePropertyValueForWidgetEvent|EncodePropertyValueFromWidgetEvent|GetPropertyOptionsEvent $event The Event for which the callback shall be invoked. - * @codingStandardsIgnoreEnd - * - * @return bool + * {@inheritDoc} */ public function wantToExecute($event) { - if (\method_exists($event, 'getPropertyName')) { - $property = $event->getPropertyName(); - } elseif ($event->getProperty() instanceof PropertyInterface) { - $property = $event->getProperty()->getName(); - } else { - $property = $event->getProperty(); + return parent::wantToExecute($event) + && (empty($this->propertyName) || ($this->propertyName === $this->getProperty($event))); + } + + private function getProperty(Event $event): string + { + if (method_exists($event, 'getPropertyName')) { + return $event->getPropertyName(); + } + if (method_exists($event, 'getProperty')) { + if ($event->getProperty() instanceof PropertyInterface) { + return $event->getProperty()->getName(); + } else { + return (string) $event->getProperty(); + } } - return parent::wantToExecute($event) - && (empty($this->propertyName) || ($this->propertyName === $property)); + throw new InvalidArgumentException('Neither Method getPropertyName() nor method getProperty() found'); } } diff --git a/src/Contao/Callback/Callbacks.php b/src/Contao/Callback/Callbacks.php index b0f09eb9..ec619782 100644 --- a/src/Contao/Callback/Callbacks.php +++ b/src/Contao/Callback/Callbacks.php @@ -24,9 +24,24 @@ use Contao\CoreBundle\Exception\ResponseException; use Contao\System; use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralRuntimeException; +use Exception; +use ReflectionClass; use Symfony\Component\DependencyInjection\Container; use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException; +use function array_shift; +use function call_user_func_array; +use function class_exists; +use function count; +use function func_get_args; +use function get_class; +use function implode; +use function is_array; +use function is_object; +use function is_string; +use function sprintf; +use function strpos; + /** * Class Callbacks. * @@ -49,9 +64,9 @@ class Callbacks public static function call($callback, $_ = null) { // Get method parameters as callback parameters. - $args = \func_get_args(); + $args = func_get_args(); // But skip $callback. - \array_shift($args); + array_shift($args); return static::callArgs($callback, $args); } @@ -72,25 +87,25 @@ public static function callArgs($callback, array $args = []) try { $callback = static::evaluateCallback($callback); - return \call_user_func_array($callback, $args); + return call_user_func_array($callback, $args); } catch (ResponseException $e) { throw $e; - } catch (\Exception $e) { + } catch (Exception $e) { $message = $e->getMessage(); } - if (\is_array($callback) && \is_object($callback[0])) { - $callback[0] = \get_class($callback[0]); + if (is_array($callback) && is_object($callback[0])) { + $callback[0] = get_class($callback[0]); } throw new DcGeneralRuntimeException( - \sprintf( + sprintf( 'Execute callback %s failed - Exception message: %s', - (\is_array($callback) - ? \implode('::', $callback) - : (\is_string($callback) + (is_array($callback) + ? implode('::', $callback) + : (is_string($callback) ? $callback - : \get_class( + : get_class( $callback ))), $message @@ -103,7 +118,7 @@ public static function callArgs($callback, array $args = []) /** * Evaluate the callback and create an object instance if required and possible. * - * @param array|callable $callback The callback to invoke. + * @param array|array{0: class-string, 1: string}|callable $callback The callback to invoke. * * @return array|callable * @@ -111,13 +126,13 @@ public static function callArgs($callback, array $args = []) */ protected static function evaluateCallback($callback) { - if (\is_array($callback) && (2 === \count($callback)) && \is_string($callback[0]) && \is_string($callback[1])) { + if (is_array($callback) && (2 === count($callback)) && class_exists($callback[0]) && is_string($callback[1])) { $serviceCallback = static::evaluateServiceCallback($callback); if ($serviceCallback[0] !== $callback[0]) { return $serviceCallback; } - $class = new \ReflectionClass($callback[0]); + $class = new ReflectionClass($callback[0]); // Ff the method is static, do not create an instance. if ($class->hasMethod($callback[1]) && $class->getMethod($callback[1])->isStatic()) { @@ -165,7 +180,7 @@ private static function evaluateServiceCallback($callback) if ( $container->has($callback[0]) - && ((false !== \strpos($callback[0], '\\')) || !\class_exists($callback[0])) + && ((false !== strpos($callback[0], '\\')) || !class_exists($callback[0])) ) { $callback[0] = $container->get($callback[0]); diff --git a/src/Contao/Callback/ContainerGetBreadcrumbCallbackListener.php b/src/Contao/Callback/ContainerGetBreadcrumbCallbackListener.php index f67f166f..a1852a98 100644 --- a/src/Contao/Callback/ContainerGetBreadcrumbCallbackListener.php +++ b/src/Contao/Callback/ContainerGetBreadcrumbCallbackListener.php @@ -27,15 +27,13 @@ * Class ContainerGetBreadcrumbCallbackListener. * * Callback handler for breadcrumbs in backend. + * + * @extends AbstractReturningCallbackListener */ class ContainerGetBreadcrumbCallbackListener extends AbstractReturningCallbackListener { /** - * Retrieve the arguments for the callback. - * - * @param GetBreadcrumbEvent $event The event being emitted. - * - * @return array + * {@inheritDoc} */ public function getArgs($event) { @@ -45,12 +43,7 @@ public function getArgs($event) } /** - * Update the information in the event with the list of breadcrumb elements returned by the callback. - * - * @param GetBreadcrumbEvent $event The event being emitted. - * @param array $value The breadcrumb elements returned by the callback. - * - * @return void + * {@inheritDoc} */ public function update($event, $value) { diff --git a/src/Contao/Callback/ContainerGlobalButtonCallbackListener.php b/src/Contao/Callback/ContainerGlobalButtonCallbackListener.php index 78aa5ccc..b8d4063b 100644 --- a/src/Contao/Callback/ContainerGlobalButtonCallbackListener.php +++ b/src/Contao/Callback/ContainerGlobalButtonCallbackListener.php @@ -27,6 +27,8 @@ * Class ContainerGlobalButtonCallbackListener. * * Handler for the global buttons. + * + * @extends AbstractReturningCallbackListener */ class ContainerGlobalButtonCallbackListener extends AbstractReturningCallbackListener { @@ -52,11 +54,7 @@ public function setRestrictions($dataContainerName = null, $operationName = null } /** - * Check the restrictions against the information within the event and determine if the callback shall be executed. - * - * @param GetGlobalButtonEvent $event The Event for which the callback shall be invoked. - * - * @return bool + * {@inheritDoc} */ public function wantToExecute($event) { @@ -67,32 +65,26 @@ public function wantToExecute($event) } /** - * Retrieve the arguments for the callback. - * - * @param GetGlobalButtonEvent $event The event being emitted. - * - * @return array + * {@inheritDoc} */ public function getArgs($event) { + if (null === $definition = $event->getEnvironment()->getDataDefinition()) { + throw new \LogicException('No data definition given.'); + } return [ $event->getHref(), $event->getLabel(), $event->getTitle(), $event->getClass(), $event->getAttributes(), - $event->getEnvironment()->getDataDefinition()->getName(), - $event->getEnvironment()->getDataDefinition()->getBasicDefinition()->getRootEntries() + $definition->getName(), + $definition->getBasicDefinition()->getRootEntries() ]; } /** - * Update the event with the information returned by the callback. - * - * @param GetGlobalButtonEvent $event The event being emitted. - * @param string $value The HTML representation of the button. - * - * @return void + * {@inheritDoc} */ public function update($event, $value) { diff --git a/src/Contao/Callback/ContainerHeaderCallbackListener.php b/src/Contao/Callback/ContainerHeaderCallbackListener.php index 3b24b70b..b932d4f8 100644 --- a/src/Contao/Callback/ContainerHeaderCallbackListener.php +++ b/src/Contao/Callback/ContainerHeaderCallbackListener.php @@ -28,15 +28,13 @@ * Class ContainerHeaderCallbackListener. * * Handler for the header row in parent list mode. + * + * @extends AbstractReturningCallbackListener */ class ContainerHeaderCallbackListener extends AbstractReturningCallbackListener { /** - * Retrieve the arguments for the callback. - * - * @param GetParentHeaderEvent $event The event being emitted. - * - * @return array + * {@inheritDoc} */ public function getArgs($event) { @@ -44,12 +42,7 @@ public function getArgs($event) } /** - * Update the event with the information returned by the callback. - * - * @param GetParentHeaderEvent $event The event being emitted. - * @param array $value The additional information. - * - * @return void + * {@inheritDoc} */ public function update($event, $value) { diff --git a/src/Contao/Callback/ContainerOnCopyCallbackListener.php b/src/Contao/Callback/ContainerOnCopyCallbackListener.php index ccff2df0..d58e879a 100644 --- a/src/Contao/Callback/ContainerOnCopyCallbackListener.php +++ b/src/Contao/Callback/ContainerOnCopyCallbackListener.php @@ -29,15 +29,13 @@ * Class ContainerOnCopyCallbackListener. * * Handle callbacks to be invoked when a copy operation is made. + * + * @extends AbstractCallbackListener */ class ContainerOnCopyCallbackListener extends AbstractCallbackListener { /** - * Retrieve the arguments for the callback. - * - * @param PostDuplicateModelEvent $event The event being emitted. - * - * @return array + * {@inheritDoc} */ public function getArgs($event) { diff --git a/src/Contao/Callback/ContainerOnCutCallbackListener.php b/src/Contao/Callback/ContainerOnCutCallbackListener.php index dfcddb80..a9773b0e 100644 --- a/src/Contao/Callback/ContainerOnCutCallbackListener.php +++ b/src/Contao/Callback/ContainerOnCutCallbackListener.php @@ -28,15 +28,13 @@ * Class ContainerOnCutCallbackListener. * * Handle callbacks to be invoked when a cut operation is made. + * + * @extends AbstractCallbackListener */ class ContainerOnCutCallbackListener extends AbstractCallbackListener { /** - * Retrieve the arguments for the callback. - * - * @param PostPasteModelEvent $event The event being emitted. - * - * @return array + * {@inheritDoc} */ public function getArgs($event) { diff --git a/src/Contao/Callback/ContainerOnDeleteCallbackListener.php b/src/Contao/Callback/ContainerOnDeleteCallbackListener.php index c2308cac..1dd3ee9f 100644 --- a/src/Contao/Callback/ContainerOnDeleteCallbackListener.php +++ b/src/Contao/Callback/ContainerOnDeleteCallbackListener.php @@ -29,15 +29,13 @@ * Class ContainerOnDeleteCallbackListener. * * Handler for delete callbacks. + * + * @extends AbstractCallbackListener */ class ContainerOnDeleteCallbackListener extends AbstractCallbackListener { /** - * Retrieve the arguments for the callback. - * - * @param PostDeleteModelEvent $event The event being emitted. - * - * @return array + * {@inheritDoc} */ public function getArgs($event) { diff --git a/src/Contao/Callback/ContainerOnLoadCallbackListener.php b/src/Contao/Callback/ContainerOnLoadCallbackListener.php index 234477f3..635cfd5c 100644 --- a/src/Contao/Callback/ContainerOnLoadCallbackListener.php +++ b/src/Contao/Callback/ContainerOnLoadCallbackListener.php @@ -28,29 +28,27 @@ * Class ContainerOnLoadCallbackListener. * * Handle onload_callbacks. + * + * @extends AbstractCallbackListener */ class ContainerOnLoadCallbackListener extends AbstractCallbackListener { /** - * Check the restrictions against the information within the event and determine if the callback shall be executed. - * - * @param CreateDcGeneralEvent $event The Event for which the callback shall be invoked. - * - * @return bool + * {@inheritDoc} */ public function wantToExecute($event) { - return (empty($this->dataContainerName) - || ($this->dataContainerName === $event->getDcGeneral()->getEnvironment()->getDataDefinition()->getName()) - ); + if (empty($this->dataContainerName)) { + return true; + } + if (null === $definition = $event->getDcGeneral()->getEnvironment()->getDataDefinition()) { + throw new \LogicException('No data definition given.'); + } + return ($this->dataContainerName === $definition->getName()); } /** - * Retrieve the arguments for the callback. - * - * @param CreateDcGeneralEvent $event The event being emitted. - * - * @return array + * {@inheritDoc} */ public function getArgs($event) { diff --git a/src/Contao/Callback/ContainerOnSubmitCallbackListener.php b/src/Contao/Callback/ContainerOnSubmitCallbackListener.php index e1129d29..9b545ad5 100644 --- a/src/Contao/Callback/ContainerOnSubmitCallbackListener.php +++ b/src/Contao/Callback/ContainerOnSubmitCallbackListener.php @@ -29,15 +29,13 @@ * Class ContainerOnSubmitCallbackListener. * * Handle onsubmit_callbacks. + * + * @extends AbstractCallbackListener */ class ContainerOnSubmitCallbackListener extends AbstractCallbackListener { /** - * Retrieve the arguments for the callback. - * - * @param PostPersistModelEvent $event The event being emitted. - * - * @return array + * {@inheritDoc} */ public function getArgs($event) { diff --git a/src/Contao/Callback/ContainerPasteButtonCallbackListener.php b/src/Contao/Callback/ContainerPasteButtonCallbackListener.php index 65359a06..8a31b6f9 100644 --- a/src/Contao/Callback/ContainerPasteButtonCallbackListener.php +++ b/src/Contao/Callback/ContainerPasteButtonCallbackListener.php @@ -28,36 +28,32 @@ * Class ContainerPasteButtonCallbackListener. * * Invoker for paste_button_callbacks. + * + * @extends AbstractReturningCallbackListener */ class ContainerPasteButtonCallbackListener extends AbstractReturningCallbackListener { /** - * Retrieve the arguments for the callback. - * - * @param GetPasteButtonEvent $event The event being emitted. - * - * @return array + * {@inheritDoc} */ public function getArgs($event) { + if (null === $definition = $event->getEnvironment()->getDataDefinition()) { + throw new \LogicException('No data definition given.'); + } return [ new DcCompat($event->getEnvironment(), $event->getModel()), - $event->getModel()->getPropertiesAsArray(), - $event->getEnvironment()->getDataDefinition()->getName(), + ($model = $event->getModel()) ? $model->getPropertiesAsArray() : [], + $definition->getName(), $event->isCircularReference(), $event->getContainedModels(), - $event->getPrevious() ? $event->getPrevious()->getId() : null, - $event->getNext() ? $event->getNext()->getId() : null + ($previous = $event->getPrevious()) ? $previous->getId() : null, + ($next = $event->getNext()) ? $next->getId() : null ]; } /** - * Set the HTML code for the button. - * - * @param GetPasteButtonEvent $event The event being emitted. - * @param string $value The value returned by the callback. - * - * @return void + * {@inheritDoc} */ public function update($event, $value) { diff --git a/src/Contao/Callback/ContainerPasteRootButtonCallbackListener.php b/src/Contao/Callback/ContainerPasteRootButtonCallbackListener.php index 28be5846..24973807 100644 --- a/src/Contao/Callback/ContainerPasteRootButtonCallbackListener.php +++ b/src/Contao/Callback/ContainerPasteRootButtonCallbackListener.php @@ -28,22 +28,27 @@ * Class ContainerPasteRootButtonCallbackListener. * * Handler for the paste into root buttons. + * + * @extends AbstractReturningCallbackListener */ class ContainerPasteRootButtonCallbackListener extends AbstractReturningCallbackListener { /** - * Retrieve the arguments for the callback. - * - * @param GetPasteRootButtonEvent $event The event being emitted. - * - * @return array + * {@inheritDoc} */ public function getArgs($event) { + if (null === $provider = $event->getEnvironment()->getDataProvider()) { + throw new \LogicException('No data provider given.'); + } + if (null === $definition = $event->getEnvironment()->getDataDefinition()) { + throw new \LogicException('No data definition given.'); + } + return [ new DcCompat($event->getEnvironment()), - $event->getEnvironment()->getDataProvider()->getEmptyModel()->getPropertiesAsArray(), - $event->getEnvironment()->getDataDefinition()->getName(), + $provider->getEmptyModel()->getPropertiesAsArray(), + $definition->getName(), false, [], null, @@ -52,12 +57,7 @@ public function getArgs($event) } /** - * Set the HTML code for the button. - * - * @param GetPasteRootButtonEvent $event The event being emitted. - * @param string $value The value returned by the callback. - * - * @return void + * {@inheritDoc} */ public function update($event, $value) { diff --git a/src/Contao/Callback/ModelChildRecordCallbackListener.php b/src/Contao/Callback/ModelChildRecordCallbackListener.php index 0eee077c..bf1e380c 100644 --- a/src/Contao/Callback/ModelChildRecordCallbackListener.php +++ b/src/Contao/Callback/ModelChildRecordCallbackListener.php @@ -27,15 +27,13 @@ * Class ModelChildRecordCallbackListener. * * Handler for the child record callbacks. + * + * @extends AbstractReturningCallbackListener */ class ModelChildRecordCallbackListener extends AbstractReturningCallbackListener { /** - * Retrieve the arguments for the callback. - * - * @param ParentViewChildRecordEvent $event The event being emitted. - * - * @return array + * {@inheritDoc} */ public function getArgs($event) { @@ -45,12 +43,7 @@ public function getArgs($event) } /** - * Set the HTML code for the button. - * - * @param ParentViewChildRecordEvent $event The event being emitted. - * @param string $value The value returned by the callback. - * - * @return void + * {@inheritDoc} */ public function update($event, $value) { diff --git a/src/Contao/Callback/ModelGroupCallbackListener.php b/src/Contao/Callback/ModelGroupCallbackListener.php index 134c3b01..cfdfe4cd 100644 --- a/src/Contao/Callback/ModelGroupCallbackListener.php +++ b/src/Contao/Callback/ModelGroupCallbackListener.php @@ -27,15 +27,13 @@ * Class ModelGroupCallbackListener. * * Handler for the group header callbacks of a property. + * + * @extends AbstractReturningCallbackListener */ class ModelGroupCallbackListener extends AbstractReturningCallbackListener { /** - * Retrieve the arguments for the callback. - * - * @param GetGroupHeaderEvent $event The event being emitted. - * - * @return array + * {@inheritDoc} */ public function getArgs($event) { @@ -48,12 +46,7 @@ public function getArgs($event) } /** - * Set the value in the event. - * - * @param GetGroupHeaderEvent $event The event being emitted. - * @param string $value The value returned by the callback. - * - * @return void + * {@inheritDoc} */ public function update($event, $value) { diff --git a/src/Contao/Callback/ModelLabelCallbackListener.php b/src/Contao/Callback/ModelLabelCallbackListener.php index 22b9a590..cf99209e 100644 --- a/src/Contao/Callback/ModelLabelCallbackListener.php +++ b/src/Contao/Callback/ModelLabelCallbackListener.php @@ -30,15 +30,13 @@ * Class ModelLabelCallbackListener. * * Handle the label_callbacks. + * + * @extends AbstractReturningCallbackListener */ class ModelLabelCallbackListener extends AbstractReturningCallbackListener { /** - * Retrieve the arguments for the callback. - * - * @param ModelToLabelEvent $event The event being emitted. - * - * @return array + * {@inheritDoc} */ public function getArgs($event) { @@ -51,12 +49,7 @@ public function getArgs($event) } /** - * Set the value in the event. - * - * @param ModelToLabelEvent $event The event being emitted. - * @param string|array $value The label text to use. - * - * @return void + * {@inheritDoc} */ public function update($event, $value) { @@ -83,11 +76,11 @@ public function update($event, $value) * Set the value in the event. * * @param ModelToLabelEvent $event The event being emitted. - * @param string $value The label text to use. + * @param string|null $value The label text to use. * * @return void */ - private function updateNonTableMode(ModelToLabelEvent $event, $value) + private function updateNonTableMode(ModelToLabelEvent $event, ?string $value): void { if (null === $value) { return; @@ -112,9 +105,9 @@ private function updateNonTableMode(ModelToLabelEvent $event, $value) * * @return void */ - private function updateTableMode(ModelToLabelEvent $event, array $arguments) + private function updateTableMode(ModelToLabelEvent $event, array $arguments): void { - if ((null === $arguments) || empty($arguments)) { + if (empty($arguments)) { return; } diff --git a/src/Contao/Callback/ModelOperationButtonCallbackListener.php b/src/Contao/Callback/ModelOperationButtonCallbackListener.php index 4753cc08..fc7fb2ff 100644 --- a/src/Contao/Callback/ModelOperationButtonCallbackListener.php +++ b/src/Contao/Callback/ModelOperationButtonCallbackListener.php @@ -25,11 +25,16 @@ use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\GetOperationButtonEvent; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\CommandInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\ToggleCommandInterface; +use LogicException; + +use function sprintf; /** * Class ModelOperationButtonCallbackListener. * * Handle the button_callbacks. + * + * @extends AbstractReturningCallbackListener */ class ModelOperationButtonCallbackListener extends AbstractReturningCallbackListener { @@ -55,11 +60,7 @@ public function setRestrictions($dataContainerName = null, $operationName = null } /** - * Check the restrictions against the information within the event and determine if the callback shall be executed. - * - * @param GetOperationButtonEvent $event The Event for which the callback shall be invoked. - * - * @return bool + * {@inheritDoc} */ public function wantToExecute($event) { @@ -74,39 +75,35 @@ public function wantToExecute($event) } /** - * Retrieve the arguments for the callback. - * - * @param GetOperationButtonEvent $event The event being emitted. - * - * @return array + * {@inheritDoc} */ public function getArgs($event) { - $extra = $event->getCommand()->getExtra(); + $command = $event->getCommand(); + assert($command instanceof CommandInterface); + $extra = $command->getExtra(); + if (null === $definition = $event->getEnvironment()->getDataDefinition()) { + throw new LogicException('No data definition given.'); + } return [ - $event->getModel()->getPropertiesAsArray(), - $this->buildHref($event->getCommand()), + ($model = $event->getModel()) ? $model->getPropertiesAsArray() : [], + $this->buildHref($command), $event->getLabel(), $event->getTitle(), ($extra['icon'] ?? null), $event->getAttributes(), - $event->getEnvironment()->getDataDefinition()->getName(), - $event->getEnvironment()->getDataDefinition()->getBasicDefinition()->getRootEntries(), + $definition->getName(), + $definition->getBasicDefinition()->getRootEntries(), $event->getChildRecordIds(), $event->isCircularReference(), - $event->getPrevious() ? $event->getPrevious()->getId() : null, - $event->getNext() ? $event->getNext()->getId() : null + ($previous = $event->getPrevious()) ? $previous->getId() : null, + ($next = $event->getNext()) ? $next->getId() : null ]; } /** - * Set the value in the event. - * - * @param GetOperationButtonEvent $event The event being emitted. - * @param string $value The value returned by the callback. - * - * @return void + * {@inheritDoc} */ public function update($event, $value) { @@ -130,7 +127,7 @@ protected function buildHref(CommandInterface $command) $strHref = ''; foreach ($arrParameters as $key => $value) { - $strHref .= \sprintf('&%s=%s', $key, $value); + $strHref .= sprintf('&%s=%s', $key, $value); } return $strHref; diff --git a/src/Contao/Callback/ModelOptionsCallbackListener.php b/src/Contao/Callback/ModelOptionsCallbackListener.php index 98aafcbf..4be5b0d9 100644 --- a/src/Contao/Callback/ModelOptionsCallbackListener.php +++ b/src/Contao/Callback/ModelOptionsCallbackListener.php @@ -28,15 +28,13 @@ * Class ModelOptionsCallbackListener. * * Handle the options_callback for a model in edit view. + * + * @extends AbstractReturningPropertyCallbackListener */ class ModelOptionsCallbackListener extends AbstractReturningPropertyCallbackListener { /** - * Retrieve the arguments for the callback. - * - * @param GetPropertyOptionsEvent $event The event being emitted. - * - * @return array + * {@inheritDoc} */ public function getArgs($event) { @@ -46,12 +44,7 @@ public function getArgs($event) } /** - * Update the options list in the event. - * - * @param GetPropertyOptionsEvent $event The event being emitted. - * @param array $value The options array. - * - * @return void + * {@inheritDoc} */ public function update($event, $value) { diff --git a/src/Contao/Callback/PropertyInputFieldCallbackListener.php b/src/Contao/Callback/PropertyInputFieldCallbackListener.php index 20002297..8330cc93 100644 --- a/src/Contao/Callback/PropertyInputFieldCallbackListener.php +++ b/src/Contao/Callback/PropertyInputFieldCallbackListener.php @@ -29,31 +29,24 @@ * Class PropertyInputFieldCallbackListener. * * Handle input_field_callbacks. + * + * @extends AbstractReturningPropertyCallbackListener */ class PropertyInputFieldCallbackListener extends AbstractReturningPropertyCallbackListener { /** - * Retrieve the arguments for the callback. - * - * @param BuildWidgetEvent $event The event being emitted. - * - * @return array + * {@inheritDoc} */ public function getArgs($event) { return [ $event->getProperty(), - new DcCompat($event->getEnvironment(), $event->getModel(), $event->getProperty()) + new DcCompat($event->getEnvironment(), $event->getModel(), $event->getProperty()->getName()) ]; } /** - * Update the widget in the event. - * - * @param BuildWidgetEvent $event The event being emitted. - * @param Widget $value The widget that has been constructed. - * - * @return void + * {@inheritDoc} */ public function update($event, $value) { diff --git a/src/Contao/Callback/PropertyInputFieldGetWizardCallbackListener.php b/src/Contao/Callback/PropertyInputFieldGetWizardCallbackListener.php index 1d59952c..31d12da9 100644 --- a/src/Contao/Callback/PropertyInputFieldGetWizardCallbackListener.php +++ b/src/Contao/Callback/PropertyInputFieldGetWizardCallbackListener.php @@ -29,16 +29,14 @@ * * Handle the property wizard callbacks. * + * @extends AbstractReturningPropertyCallbackListener + * * @SuppressWarnings(PHPMD.LongClassName) */ class PropertyInputFieldGetWizardCallbackListener extends AbstractReturningPropertyCallbackListener { /** - * Retrieve the arguments for the callback. - * - * @param BuildWidgetEvent $event The event being emitted. - * - * @return array + * {@inheritDoc} */ public function getArgs($event) { @@ -48,12 +46,7 @@ public function getArgs($event) } /** - * Update the wizard HTML string in the widget. - * - * @param BuildWidgetEvent $event The event being emitted. - * @param string $value The HTML for the wizard of the widget. - * - * @return void + * {@inheritDoc} */ public function update($event, $value) { diff --git a/src/Contao/Callback/PropertyInputFieldGetXLabelCallbackListener.php b/src/Contao/Callback/PropertyInputFieldGetXLabelCallbackListener.php index 91b4dbbf..5449ef86 100644 --- a/src/Contao/Callback/PropertyInputFieldGetXLabelCallbackListener.php +++ b/src/Contao/Callback/PropertyInputFieldGetXLabelCallbackListener.php @@ -28,31 +28,24 @@ * * Handle the property wizard callbacks. * + * @extends AbstractReturningPropertyCallbackListener + * * @SuppressWarnings(PHPMD.LongClassName) */ class PropertyInputFieldGetXLabelCallbackListener extends AbstractReturningPropertyCallbackListener { /** - * Retrieve the arguments for the callback. - * - * @param BuildWidgetEvent $event The event being emitted. - * - * @return array + * {@inheritDoc} */ public function getArgs($event) { return [ - new DcCompat($event->getEnvironment(), $event->getModel(), $event->getProperty()) + new DcCompat($event->getEnvironment(), $event->getModel(), $event->getProperty()->getName()) ]; } /** - * Update the wizard HTML string in the widget. - * - * @param BuildWidgetEvent $event The event being emitted. - * @param string $value The HTML for the wizard of the widget. - * - * @return void + * {@inheritDoc} */ public function update($event, $value) { diff --git a/src/Contao/Callback/PropertyOnLoadCallbackListener.php b/src/Contao/Callback/PropertyOnLoadCallbackListener.php index 265491a5..f9a98d49 100644 --- a/src/Contao/Callback/PropertyOnLoadCallbackListener.php +++ b/src/Contao/Callback/PropertyOnLoadCallbackListener.php @@ -28,15 +28,13 @@ * Class PropertyOnLoadCallbackListener. * * Handler for the load_callbacks of a property. + * + * @extends AbstractReturningPropertyCallbackListener */ class PropertyOnLoadCallbackListener extends AbstractReturningPropertyCallbackListener { /** - * Retrieve the arguments for the callback. - * - * @param DecodePropertyValueForWidgetEvent $event The event being emitted. - * - * @return array + * {@inheritDoc} */ public function getArgs($event) { @@ -47,12 +45,7 @@ public function getArgs($event) } /** - * Update the value in the event. - * - * @param DecodePropertyValueForWidgetEvent $event The event being emitted. - * @param mixed $value The decoded value. - * - * @return void + * {@inheritDoc} */ public function update($event, $value) { diff --git a/src/Contao/Callback/PropertyOnSaveCallbackListener.php b/src/Contao/Callback/PropertyOnSaveCallbackListener.php index 3aea164a..cbaf8472 100644 --- a/src/Contao/Callback/PropertyOnSaveCallbackListener.php +++ b/src/Contao/Callback/PropertyOnSaveCallbackListener.php @@ -28,15 +28,13 @@ * Class PropertyOnSaveCallbackListener. * * Handler for the save_callbacks of a property. + * + * @extends AbstractReturningPropertyCallbackListener */ class PropertyOnSaveCallbackListener extends AbstractReturningPropertyCallbackListener { /** - * Retrieve the arguments for the callback. - * - * @param EncodePropertyValueFromWidgetEvent $event The event being emitted. - * - * @return array + * {@inheritDoc} */ public function getArgs($event) { @@ -47,12 +45,7 @@ public function getArgs($event) } /** - * Update the value in the event. - * - * @param EncodePropertyValueFromWidgetEvent $event The event being emitted. - * @param mixed $value The encoded value. - * - * @return void + * {@inheritDoc} */ public function update($event, $value) { From 8d7ac19b43f1f4735f7543194b1ab15853aa93b7 Mon Sep 17 00:00:00 2001 From: Christian Schiffler Date: Wed, 18 Jan 2023 12:30:07 +0100 Subject: [PATCH 31/53] Fix psalm issues in events --- .../Event/GetOperationButtonEvent.php | 32 ++++----- .../Event/GetPasteButtonEvent.php | 66 +++++++++---------- src/Event/AbstractViewAwareEvent.php | 6 +- src/Event/ActionEvent.php | 5 +- src/Event/EnforceModelRelationshipEvent.php | 4 +- src/Event/FormatModelLabelEvent.php | 2 +- src/Event/InvalidHttpCacheTagsEvent.php | 10 ++- src/Event/ViewEvent.php | 8 ++- 8 files changed, 72 insertions(+), 61 deletions(-) diff --git a/src/Contao/View/Contao2BackendView/Event/GetOperationButtonEvent.php b/src/Contao/View/Contao2BackendView/Event/GetOperationButtonEvent.php index 88a12eaf..a356f19e 100644 --- a/src/Contao/View/Contao2BackendView/Event/GetOperationButtonEvent.php +++ b/src/Contao/View/Contao2BackendView/Event/GetOperationButtonEvent.php @@ -37,56 +37,56 @@ class GetOperationButtonEvent extends BaseButtonEvent /** * The command for which the button is being rendered. * - * @var CommandInterface + * @var CommandInterface|null */ protected $command; /** * The model to which the command shall be applied to. * - * @var ModelInterface + * @var ModelInterface|null */ protected $model; /** * The ids of any child records of the model. * - * @var array + * @var array|null */ protected $childRecordIds; /** * Determinator if there is a circular reference from an item in the clipboard to the current model. * - * @var bool + * @var bool|null */ protected $circularReference; /** * The next model succeeding the current model. * - * @var ModelInterface + * @var ModelInterface|null */ protected $next; /** * The model preceeding the current model. * - * @var ModelInterface + * @var ModelInterface|null */ protected $previous; /** * The href for the command. * - * @var string + * @var string|null */ protected $href; /** * Disabled state of the button. * - * @var bool + * @var bool|null */ protected $disabled; @@ -107,7 +107,7 @@ public function setCommand($objCommand) /** * Retrieve the attached command. * - * @return CommandInterface + * @return CommandInterface|null */ public function getCommand() { @@ -131,7 +131,7 @@ public function setChildRecordIds($childRecordIds) /** * Retrieve the ids of the child records of the current model. * - * @return array + * @return array|null */ public function getChildRecordIds() { @@ -161,7 +161,7 @@ public function setCircularReference($circularReference) * This flag determines if there exists a circular reference between the item currently in the clipboard and the * current model. A circular reference is of relevance when performing a cut and paste operation for example. * - * @return boolean + * @return bool|null */ public function isCircularReference() { @@ -185,7 +185,7 @@ public function setNext($next) /** * Get the next model in the list, succeeding the current model. * - * @return ModelInterface + * @return ModelInterface|null */ public function getNext() { @@ -209,7 +209,7 @@ public function setPrevious($previous) /** * Get the previous model in the list, preceding the current model. * - * @return ModelInterface + * @return ModelInterface|null */ public function getPrevious() { @@ -233,7 +233,7 @@ public function setHref($href) /** * Retrieve the href for the button. * - * @return string + * @return string|null */ public function getHref() { @@ -257,7 +257,7 @@ public function setObjModel($model) /** * Get the model currently in scope. * - * @return ModelInterface + * @return ModelInterface|null */ public function getModel() { @@ -281,7 +281,7 @@ public function setDisabled($disabled = true) /** * Determine if the command is disabled. * - * @return boolean + * @return bool|null */ public function isDisabled() { diff --git a/src/Contao/View/Contao2BackendView/Event/GetPasteButtonEvent.php b/src/Contao/View/Contao2BackendView/Event/GetPasteButtonEvent.php index 07f56603..c0e7a3c4 100644 --- a/src/Contao/View/Contao2BackendView/Event/GetPasteButtonEvent.php +++ b/src/Contao/View/Contao2BackendView/Event/GetPasteButtonEvent.php @@ -36,79 +36,79 @@ class GetPasteButtonEvent extends BaseButtonEvent /** * Determinator if there is a circular reference from an item in the clipboard to the current model. * - * @var bool + * @var bool|null */ - protected $circularReference; + protected $circularReference = null; /** * The href information to use for the paste after button. * - * @var string + * @var string|null */ - protected $hrefAfter; + protected $hrefAfter = null; /** * The href information to use for the paste into button. * - * @var string + * @var string|null */ - protected $hrefInto; + protected $hrefInto = null; /** * The Html code to use for the "paste after" button. * - * @var string + * @var string|null */ - protected $htmlPasteAfter; + protected $htmlPasteAfter = null; /** * The Html code to use for the "paste into" button. * - * @var string + * @var string|null */ - protected $htmlPasteInto; + protected $htmlPasteInto = null; /** * The model to which the command shall be applied to. * - * @var ModelInterface + * @var ModelInterface|null */ - protected $model; + protected $model = null; /** * The next model in the list. * - * @var ModelInterface + * @var ModelInterface|null */ - protected $next; + protected $next = null; /** * The previous model in the list. * - * @var ModelInterface + * @var ModelInterface|null */ - protected $previous; + protected $previous = null; /** * Determinator if the paste into button shall be disabled. * - * @var bool + * @var bool|null */ - protected $pasteIntoDisabled; + protected $pasteIntoDisabled = null; /** * Determinator if the paste after button shall be disabled. * - * @var bool + * @var bool|null */ - protected $pasteAfterDisabled; + protected $pasteAfterDisabled = null; /** * The models currently in the clipboard. * - * @var CollectionInterface + * @var CollectionInterface|null */ - protected $containedModels; + protected $containedModels = null; /** * Set determinator if there exists a circular reference. @@ -133,7 +133,7 @@ public function setCircularReference($circularReference) * This flag determines if there exists a circular reference between the item currently in the clipboard and the * current model. A circular reference is of relevance when performing a cut and paste operation for example. * - * @return boolean + * @return bool|null */ public function isCircularReference() { @@ -157,7 +157,7 @@ public function setHrefAfter($hrefAfter) /** * Get the href for the paste after button. * - * @return string + * @return string|null */ public function getHrefAfter() { @@ -181,7 +181,7 @@ public function setHrefInto($hrefInto) /** * Get the href for the paste into button. * - * @return string + * @return string|null */ public function getHrefInto() { @@ -205,7 +205,7 @@ public function setHtmlPasteAfter($html) /** * Get the html code for the paste after button. * - * @return string + * @return string|null */ public function getHtmlPasteAfter() { @@ -229,7 +229,7 @@ public function setHtmlPasteInto($html) /** * Get the html code for the paste after button. * - * @return string + * @return string|null */ public function getHtmlPasteInto() { @@ -253,7 +253,7 @@ public function setModel($model) /** * Get the model currently in scope. * - * @return ModelInterface + * @return ModelInterface|null */ public function getModel() { @@ -277,7 +277,7 @@ public function setNext($next) /** * Get the next model. * - * @return ModelInterface + * @return ModelInterface|null */ public function getNext() { @@ -301,7 +301,7 @@ public function setPrevious($previous) /** * Get the previous model. * - * @return ModelInterface + * @return ModelInterface|null */ public function getPrevious() { @@ -325,7 +325,7 @@ public function setPasteAfterDisabled($pasteAfterDisabled) /** * Check if the paste after button shall be disabled. * - * @return boolean + * @return bool|null */ public function isPasteAfterDisabled() { @@ -349,7 +349,7 @@ public function setPasteIntoDisabled($pasteIntoDisabled) /** * Check if the paste into button shall be disabled. * - * @return boolean + * @return bool|null */ public function isPasteIntoDisabled() { @@ -359,7 +359,7 @@ public function isPasteIntoDisabled() /** * Retrieve the collection of contained models. * - * @return CollectionInterface + * @return CollectionInterface|null */ public function getContainedModels() { diff --git a/src/Event/AbstractViewAwareEvent.php b/src/Event/AbstractViewAwareEvent.php index fc4bbaab..8f051629 100644 --- a/src/Event/AbstractViewAwareEvent.php +++ b/src/Event/AbstractViewAwareEvent.php @@ -21,6 +21,7 @@ namespace ContaoCommunityAlliance\DcGeneral\Event; +use ContaoCommunityAlliance\DcGeneral\View\ViewInterface; use ContaoCommunityAlliance\DcGeneral\ViewAwareInterface; /** @@ -33,6 +34,9 @@ class AbstractViewAwareEvent extends AbstractEnvironmentAwareEvent implements Vi */ public function getView() { - return $this->getEnvironment()->getView(); + $view = $this->getEnvironment()->getView(); + assert($view instanceof ViewInterface); + + return $view; } } diff --git a/src/Event/ActionEvent.php b/src/Event/ActionEvent.php index d4bb5528..5599e9b4 100644 --- a/src/Event/ActionEvent.php +++ b/src/Event/ActionEvent.php @@ -31,17 +31,18 @@ class ActionEvent extends AbstractActionAwareEvent * * @var string|null */ - protected $response; + protected $response = null; /** * Set the action response. * - * @param string $response The response. + * @param string|null $response The response. * * @return ActionEvent */ public function setResponse($response) { + /** @psalm-suppress RedundantCastGivenDocblockType - only redundant when strict typed */ $this->response = (null !== $response) ? (string) $response : null; return $this; } diff --git a/src/Event/EnforceModelRelationshipEvent.php b/src/Event/EnforceModelRelationshipEvent.php index 466739bb..ceb23c60 100644 --- a/src/Event/EnforceModelRelationshipEvent.php +++ b/src/Event/EnforceModelRelationshipEvent.php @@ -47,8 +47,8 @@ class EnforceModelRelationshipEvent extends AbstractModelAwareEvent * * @param EnvironmentInterface $environment The environment. * @param ModelInterface $model The model attached to the event. - * @param ModelInterface $parentModel The parent model (if model is parented). - * @param ModelInterface $rootModel The root model (if model is in a tree). + * @param ModelInterface|null $parentModel The parent model (if model is parented). + * @param ModelInterface|null $rootModel The root model (if model is in a tree). */ public function __construct( EnvironmentInterface $environment, diff --git a/src/Event/FormatModelLabelEvent.php b/src/Event/FormatModelLabelEvent.php index 5fa22eac..84b027a0 100644 --- a/src/Event/FormatModelLabelEvent.php +++ b/src/Event/FormatModelLabelEvent.php @@ -31,7 +31,7 @@ class FormatModelLabelEvent extends AbstractModelAwareEvent * * @var array */ - protected $label; + protected $label = []; /** * Returns the model label. diff --git a/src/Event/InvalidHttpCacheTagsEvent.php b/src/Event/InvalidHttpCacheTagsEvent.php index 84e5eb4a..abbd4529 100644 --- a/src/Event/InvalidHttpCacheTagsEvent.php +++ b/src/Event/InvalidHttpCacheTagsEvent.php @@ -29,16 +29,16 @@ class InvalidHttpCacheTagsEvent extends AbstractEnvironmentAwareEvent /** * The http cache namespace. * - * @var string + * @var string|null */ - private $namespace; + private ?string $namespace = null; /** * The cache tags. * * @var array */ - private $tags; + private array $tags = []; /** * Get the http cache namespace. @@ -47,6 +47,10 @@ class InvalidHttpCacheTagsEvent extends AbstractEnvironmentAwareEvent */ public function getNamespace(): string { + if (null === $this->namespace) { + throw new \RuntimeException('The namespace is not set.'); + } + return $this->namespace; } diff --git a/src/Event/ViewEvent.php b/src/Event/ViewEvent.php index 2107805b..bf59a211 100644 --- a/src/Event/ViewEvent.php +++ b/src/Event/ViewEvent.php @@ -46,7 +46,7 @@ class ViewEvent extends AbstractActionAwareEvent /** * The action response, if any is set. * - * @var string + * @var string|null */ protected $response; @@ -61,6 +61,7 @@ class ViewEvent extends AbstractActionAwareEvent public function __construct(EnvironmentInterface $environment, Action $action, $viewName, array $context) { parent::__construct($environment, $action); + /** @psalm-suppress RedundantCastGivenDocblockType - only redundant when strict typed */ $this->viewName = (string) $viewName; $this->context = $context; } @@ -101,12 +102,13 @@ public function setContext(array $context) /** * Set the action response. * - * @param string $response The response. + * @param string|null $response The response. * * @return ViewEvent */ public function setResponse($response) { + /** @psalm-suppress RedundantCastGivenDocblockType - only redundant when strict typed */ $this->response = (null !== $response) ? (string) $response : null; return $this; } @@ -114,7 +116,7 @@ public function setResponse($response) /** * Return the action response. * - * @return string + * @return string|null */ public function getResponse() { From ac70b883e10f549e380cec7dbaab3cd75f93b594 Mon Sep 17 00:00:00 2001 From: Christian Schiffler Date: Wed, 18 Jan 2023 15:29:56 +0100 Subject: [PATCH 32/53] Fix psalm issues in dca builders --- .../DcaReadingDataDefinitionBuilder.php | 23 ++- ...ExtendedLegacyDcaDataDefinitionBuilder.php | 48 ++++--- .../Legacy/LegacyDcaDataDefinitionBuilder.php | 135 ++++++++++-------- 3 files changed, 126 insertions(+), 80 deletions(-) diff --git a/src/Contao/Dca/Builder/Legacy/DcaReadingDataDefinitionBuilder.php b/src/Contao/Dca/Builder/Legacy/DcaReadingDataDefinitionBuilder.php index 446cdd71..107078dd 100644 --- a/src/Contao/Dca/Builder/Legacy/DcaReadingDataDefinitionBuilder.php +++ b/src/Contao/Dca/Builder/Legacy/DcaReadingDataDefinitionBuilder.php @@ -27,6 +27,12 @@ use ContaoCommunityAlliance\DcGeneral\DataDefinition\Builder\AbstractEventDrivenDataDefinitionBuilder; use Symfony\Component\EventDispatcher\EventDispatcherInterface; +use function array_key_exists; +use function array_shift; +use function explode; +use function is_array; +use function trim; + /** * Build the container config from legacy DCA syntax. */ @@ -35,12 +41,17 @@ abstract class DcaReadingDataDefinitionBuilder extends AbstractEventDrivenDataDe /** * Buffer for the DCA. * - * @var array + * @var array|null */ - protected $dca; + protected $dca = null; /** - * {@inheritdoc} + * Load the dca data. + * + * @param string $dcaName The name of the data container. + * @param EventDispatcherInterface $dispatcher The event dispatcher to use. + * + * @return bool * * @SuppressWarnings(PHPMD.Superglobals) * @SuppressWarnings(PHPMD.CamelCaseVariableName) @@ -70,11 +81,11 @@ public function loadDca($dcaName, EventDispatcherInterface $dispatcher) */ protected function getFromDca($path) { - $chunks = \explode('/', \trim($path, '/')); + $chunks = explode('/', trim($path, '/')); $dca = $this->dca; - while (null !== ($chunk = \array_shift($chunks))) { - if (!(\is_array($dca) && \array_key_exists($chunk, $dca))) { + while (null !== ($chunk = array_shift($chunks))) { + if (!(is_array($dca) && array_key_exists($chunk, $dca))) { return null; } diff --git a/src/Contao/Dca/Builder/Legacy/ExtendedLegacyDcaDataDefinitionBuilder.php b/src/Contao/Dca/Builder/Legacy/ExtendedLegacyDcaDataDefinitionBuilder.php index 77e160f4..17f1655b 100644 --- a/src/Contao/Dca/Builder/Legacy/ExtendedLegacyDcaDataDefinitionBuilder.php +++ b/src/Contao/Dca/Builder/Legacy/ExtendedLegacyDcaDataDefinitionBuilder.php @@ -28,6 +28,7 @@ use ContaoCommunityAlliance\DcGeneral\Contao\Dca\ContaoDataProviderInformation; use ContaoCommunityAlliance\DcGeneral\Contao\Dca\Definition\ExtendedDca; use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\DataProviderInformationInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\DataProviderDefinitionInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\DefaultBasicDefinition; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\DefaultDataProviderDefinition; @@ -47,6 +48,8 @@ use ContaoCommunityAlliance\DcGeneral\Factory\DcGeneralFactory; use ContaoCommunityAlliance\DcGeneral\Factory\Event\BuildDataDefinitionEvent; use ContaoCommunityAlliance\DcGeneral\Factory\Event\PopulateEnvironmentEvent; +use ContaoCommunityAlliance\Translator\TranslatorInterface; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; /** * Build the container config from legacy DCA syntax. @@ -105,40 +108,42 @@ protected function loadAdditionalDefinitions(ContainerInterface $container) function (PopulateEnvironmentEvent $event) use ($dataContainerName) { $environment = $event->getEnvironment(); $definition = $environment->getDataDefinition(); + $dispatcher = $environment->getEventDispatcher(); + $translator = $environment->getTranslator(); + assert($definition instanceof ContainerInterface); + assert($dispatcher instanceof EventDispatcherInterface); + assert($translator instanceof TranslatorInterface); if ($definition->getName() !== $dataContainerName) { return; } $parentName = $definition->getBasicDefinition()->getParentDataProvider(); if ($parentName) { - if ($parentName === $definition->getName()) { - $parentDefinition = $definition; - } else { - $parentDefinition = (new DcGeneralFactory()) - ->setEventDispatcher($environment->getEventDispatcher()) - ->setTranslator($environment->getTranslator()) + $parentDefinition = ($parentName === $definition->getName()) + ? $definition + : (new DcGeneralFactory()) + ->setEventDispatcher($dispatcher) + ->setTranslator($translator) ->setContainerName($parentName) ->createDcGeneral() ->getEnvironment() ->getDataDefinition(); - } + assert($parentDefinition instanceof ContainerInterface); $environment->setParentDataDefinition($parentDefinition); } $rootName = $definition->getBasicDefinition()->getRootDataProvider(); if ($rootName) { - if ($rootName === $definition->getName()) { - $rootDefinition = $definition; - } else { - $rootDefinition = (new DcGeneralFactory()) - ->setEventDispatcher($environment->getEventDispatcher()) - ->setTranslator($environment->getTranslator()) + $rootDefinition = ($rootName === $definition->getName()) + ? $definition + : (new DcGeneralFactory()) + ->setEventDispatcher($dispatcher) + ->setTranslator($translator) ->setContainerName($rootName) ->createDcGeneral() ->getEnvironment() ->getDataDefinition(); - } - + assert($rootDefinition instanceof ContainerInterface); $environment->setRootDataDefinition($rootDefinition); } } @@ -208,7 +213,7 @@ protected function isSpecialName($name) * @param string|null $name The name of the data provider to be used within the * container. * - * @return ContaoDataProviderInformation|null + * @return DataProviderInformationInterface|null * * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ @@ -241,6 +246,9 @@ protected function parseSingleDataProvider( $providerInformation = $providers->getInformation($providerName); } + if (!$providerInformation instanceof ContaoDataProviderInformation) { + return $providerInformation; + } if (!$providerInformation->getTableName()) { if (isset($information['source'])) { $providerInformation @@ -481,6 +489,11 @@ protected function parseConditions(ContainerInterface $container) $definition = new DefaultModelRelationshipDefinition(); $container->setDefinition(ModelRelationshipDefinitionInterface::NAME, $definition); } + if (!$definition instanceof ModelRelationshipDefinitionInterface) { + throw new DcGeneralInvalidArgumentException( + 'Configured ModelRelationshipDefinition does not implement ModelRelationshipDefinitionInterface.' + ); + } $this->parseRootCondition($container, $definition); $this->parseParentChildConditions($definition); @@ -605,7 +618,8 @@ protected function parseDynamicParentTableProperty(ContainerInterface $container ) ); - $backendView = $container->getDefinition(Contao2BackendViewDefinition::NAME); + $backendView = $container->getDefinition(Contao2BackendViewDefinitionInterface::NAME); + assert($backendView instanceof Contao2BackendViewDefinitionInterface); $backendView->getListingConfig()->setParentTablePropertyName($propertyName); $container->getBasicDefinition()->setDynamicParentTable($dynamicParentTable); } diff --git a/src/Contao/Dca/Builder/Legacy/LegacyDcaDataDefinitionBuilder.php b/src/Contao/Dca/Builder/Legacy/LegacyDcaDataDefinitionBuilder.php index c118bd3d..fc683e59 100644 --- a/src/Contao/Dca/Builder/Legacy/LegacyDcaDataDefinitionBuilder.php +++ b/src/Contao/Dca/Builder/Legacy/LegacyDcaDataDefinitionBuilder.php @@ -90,6 +90,7 @@ use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\Panel\DefaultSearchElementInformation; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\Panel\DefaultSortElementInformation; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\Panel\DefaultSubmitElementInformation; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\Panel\SearchElementInformationInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\Panel\SubmitElementInformationInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\PanelRowCollectionInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\PanelRowInterface; @@ -111,6 +112,20 @@ use ContaoCommunityAlliance\DcGeneral\Factory\Event\CreateDcGeneralEvent; use Symfony\Component\EventDispatcher\EventDispatcherInterface; +use function array_key_exists; +use function array_keys; +use function array_merge; +use function array_merge_recursive; +use function count; +use function explode; +use function in_array; +use function is_array; +use function is_callable; +use function next; +use function parse_str; +use function reset; +use function trigger_error; + /** * Build the container config from legacy DCA syntax. * @@ -150,18 +165,22 @@ public function build(ContainerInterface $container, BuildDataDefinitionEvent $e * @param array $callbacks The callbacks to be handled. * @param string $eventName The event to be registered to. * @param array $arguments The arguments to pass to the constructor. - * @param string $listener The listener class to use. + * @param class-string $listener The listener class to use. * * @return void + * + * @psalm-suppress DocblockTypeContradiction - only redundant when strict types active. + * @psalm-suppress RedundantConditionGivenDocblockType - only redundant when strict types active. + * @psalm-suppress RedundantCastGivenDocblockType - only redundant when strict types active. */ protected function parseCallback($dispatcher, $callbacks, $eventName, $arguments, $listener) { - if (!(\is_array($callbacks) || \is_callable($callbacks))) { + if (!(is_array($callbacks) || is_callable($callbacks))) { return; } // If only one callback given, ensure the loop below handles it correctly. - if (\is_array($callbacks) && (2 === \count($callbacks)) && !\is_array($callbacks[0])) { + if (is_array($callbacks) && (2 === count($callbacks)) && !is_array($callbacks[0])) { $callbacks = [$callbacks]; } foreach ((array) $callbacks as $callback) { @@ -187,7 +206,7 @@ protected function parseCallback($dispatcher, $callbacks, $eventName, $arguments private function isCallbackBlacklisted($callback, $listener) { return ((ContainerOnLoadCallbackListener::class === $listener) - && \is_array($callback) + && is_array($callback) && ('checkPermission' === $callback[1]) && (0 === strpos($callback[0], 'tl_'))); } @@ -313,7 +332,7 @@ protected function parseCallbacks(ContainerInterface $container, EventDispatcher ] as $name => $callback ) { if ($callbacks = $this->getFromDca($name)) { - if (isset($callback['event'])) { + if (isset($callback['event']) && isset($callback['class'])) { $this->parseCallback($dispatcher, $callbacks, $callback['event'], $args, $callback['class']); continue; @@ -321,11 +340,12 @@ protected function parseCallbacks(ContainerInterface $container, EventDispatcher if (isset($callback['deprecated'])) { // @codingStandardsIgnoreStart - @\trigger_error($callback['deprecated']); + @trigger_error($callback['deprecated']); // @codingStandardsIgnoreEnd continue; } + /** @var list $callback */ foreach ($callback as $sub) { $this->parseCallback($dispatcher, $callbacks, $sub['event'], $args, $sub['class']); } @@ -459,11 +479,11 @@ protected function parseBasicDefinition(ContainerInterface $container) if ( (null !== ($filters = $this->getFromDca('list/sorting/filter'))) - && \is_array($filters) + && is_array($filters) && !empty($filters) ) { if ($config->hasAdditionalFilter()) { - $builder = FilterBuilder::fromArrayForRoot($config->getAdditionalFilter())->getFilter(); + $builder = FilterBuilder::fromArrayForRoot($config->getAdditionalFilter() ?? [])->getFilter(); } else { $builder = FilterBuilder::fromArrayForRoot()->getFilter(); } @@ -472,7 +492,7 @@ protected function parseBasicDefinition(ContainerInterface $container) $builder->andPropertyEquals($filter[0], $filter[1]); } - $config->setAdditionalFilter($config->getDataProvider(), $builder->getAllAsArray()); + $config->setAdditionalFilter((string) $config->getDataProvider(), $builder->getAllAsArray()); } } @@ -519,7 +539,7 @@ protected function parseDataProvider(ContainerInterface $container) $providerInformation ->setTableName($parentTable) ->setInitializationData( - \array_merge( + array_merge( [ 'source' => $parentTable, 'name' => $parentTable, @@ -555,7 +575,7 @@ protected function parseDataProvider(ContainerInterface $container) $providerInformation ->setTableName($providerName) ->setInitializationData( - \array_merge( + array_merge( [ 'source' => $providerName, 'name' => $providerName @@ -567,7 +587,7 @@ protected function parseDataProvider(ContainerInterface $container) $providerInformation->setVersioningEnabled(false); if (true === (bool) $this->getFromDca('config/enableVersioning')) { // @codingStandardsIgnoreStart - @\trigger_error( + @trigger_error( 'Versioning is not supported yet and will get implemented in a future release.', E_USER_WARNING ); @@ -589,10 +609,10 @@ protected function parseDataProvider(ContainerInterface $container) */ protected function parseRootEntries(ContainerInterface $container) { - if (\is_array($root = $this->getFromDca('list/sorting/root'))) { - $entries = $container->getBasicDefinition()->getRootEntries(); + if (is_array($root = $this->getFromDca('list/sorting/root'))) { + $entries = $container->getBasicDefinition()->getRootEntries() ?? []; - $container->getBasicDefinition()->setRootEntries(\array_merge($entries, $root)); + $container->getBasicDefinition()->setRootEntries(array_merge($entries, $root)); } } @@ -631,11 +651,10 @@ protected function getRootProviderName(ContainerInterface $container) */ protected function parseParentChildConditions(ContainerInterface $container) { - if ($container->hasDefinition(ModelRelationshipDefinitionInterface::NAME)) { - $definition = $container->getDefinition(ModelRelationshipDefinitionInterface::NAME); - } else { - $definition = new DefaultModelRelationshipDefinition(); - } + $definition = $container->hasDefinition(ModelRelationshipDefinitionInterface::NAME) + ? $container->getDefinition(ModelRelationshipDefinitionInterface::NAME) + : new DefaultModelRelationshipDefinition(); + assert($definition instanceof ModelRelationshipDefinitionInterface); // If mode is 5, we need to define tree view. if (5 === $this->getFromDca('list/sorting/mode')) { @@ -654,10 +673,10 @@ protected function parseParentChildConditions(ContainerInterface $container) $relationship ->setSetters( - \array_merge_recursive( - [['property' => 'pid', 'value' => '0']] + array_merge_recursive( + [['property' => 'pid', 'value' => '0']], + $relationship->getSetters() ), - $relationship->getSetters() ); $builder->andPropertyEquals('pid', '0'); @@ -679,10 +698,10 @@ protected function parseParentChildConditions(ContainerInterface $container) $relationship ->setSetters( - \array_merge_recursive( - [['to_field' => 'pid', 'from_field' => 'id']] + array_merge_recursive( + [['to_field' => 'pid', 'from_field' => 'id']], + $relationship->getSetters() ), - $relationship->getSetters() ); $builder->andRemotePropertyEquals('pid', 'id'); @@ -707,10 +726,10 @@ protected function parseParentChildConditions(ContainerInterface $container) if (!$relationship->getSetters()) { $relationship ->setSetters( - \array_merge_recursive( - [['property' => 'pid', 'value' => '0']] + array_merge_recursive( + [['property' => 'pid', 'value' => '0']], + $relationship->getSetters() ), - $relationship->getSetters() ); } @@ -811,7 +830,7 @@ protected function parsePropertySortingAndGroupings($view, $parsedProperties) */ protected function parsePropertySortingAndGrouping($propName, $propInfo, $definitions, $parsedProperties) { - if (empty($propInfo['sorting']) || \in_array($propName, $parsedProperties)) { + if (empty($propInfo['sorting']) || in_array($propName, $parsedProperties)) { return; } @@ -913,7 +932,7 @@ protected function parseListSorting(ListingConfigInterface $listing, array $list $this->evalFlagGroupingLength($groupAndSorting, $flag); } - if (1 === \count($sortingDca['fields'])) { + if (1 === count($sortingDca['fields'])) { $definition->setName($groupAndSorting->getProperty()); $parsedProperties[] = $groupAndSorting->getProperty(); } @@ -958,7 +977,7 @@ protected function parseListLabel(ContainerInterface $container, ListingConfigIn } if ($configured) { - $listing->setLabelFormatter($container->getBasicDefinition()->getDataProvider(), $formatter); + $listing->setLabelFormatter((string) $container->getBasicDefinition()->getDataProvider(), $formatter); } if (isset($labelDca['showColumns'])) { @@ -1014,6 +1033,7 @@ protected function parsePanelSearch(PanelRowInterface $row) } else { $element = new DefaultSearchElementInformation(); } + assert($element instanceof SearchElementInformationInterface); foreach ($this->getFromDca('fields') as $property => $value) { if (isset($value['search'])) { $element->addProperty($property); @@ -1062,7 +1082,7 @@ protected function parsePanelSubmit(PanelRowInterface $row) */ protected function parsePanelRow(PanelRowInterface $row, $elementList) { - foreach (\explode(',', $elementList) as $element) { + foreach (explode(',', $elementList) as $element) { switch ($element) { case 'filter': $this->parsePanelFilter($row); @@ -1101,7 +1121,7 @@ protected function parsePanel(Contao2BackendViewDefinitionInterface $view) $layout = $view->getPanelLayout(); $rows = $layout->getRows(); - foreach (\explode(';', (string) $this->getFromDca('list/sorting/panelLayout')) as $rowNo => $elementRow) { + foreach (explode(';', (string) $this->getFromDca('list/sorting/panelLayout')) as $rowNo => $elementRow) { if ($rows->getRowCount() < ($rowNo + 1)) { $row = $rows->addRow(); } else { @@ -1156,11 +1176,11 @@ protected function parseGlobalOperations(Contao2BackendViewDefinitionInterface $ $collection->addCommand(new BackCommand()); $collection->addCommand(new CreateModelCommand()); - if (!\is_array($operationsDca)) { + if (!is_array($operationsDca)) { return; } - foreach (\array_keys($operationsDca) as $operationName) { + foreach (array_keys($operationsDca) as $operationName) { $command = $this->createCommand($operationName, $operationsDca[$operationName]); $collection->addCommand($command); } @@ -1177,7 +1197,7 @@ protected function parseModelOperations(Contao2BackendViewDefinitionInterface $v { $operationsDca = $this->getFromDca('list/operations'); - if (!\is_array($operationsDca)) { + if (!is_array($operationsDca)) { return; } @@ -1204,12 +1224,12 @@ protected function parsePalettes(ContainerInterface $container) $subPalettesDefinitionArray = $this->getFromDca('subpalettes'); // Skip while there is no legacy palette definition. - if (!\is_array($palettesDefinitionArray)) { + if (!is_array($palettesDefinitionArray)) { return; } // Ignore non-legacy sub palette definition. - if (!\is_array($subPalettesDefinitionArray)) { + if (!is_array($subPalettesDefinitionArray)) { $subPalettesDefinitionArray = []; } @@ -1219,6 +1239,7 @@ protected function parsePalettes(ContainerInterface $container) $palettesDefinition = new DefaultPalettesDefinition(); $container->setDefinition(PalettesDefinitionInterface::NAME, $palettesDefinition); } + assert($palettesDefinition instanceof PalettesDefinitionInterface); $palettesParser = new LegacyPalettesParser(); $palettesParser->parse( @@ -1284,10 +1305,10 @@ private function parseOderPropertyInPalette(ContainerInterface $container) protected function parseSinglePropertyLabel(PropertyInterface $property, $label) { if (!$property->getLabel()) { - if (\is_array($label)) { + if (is_array($label)) { $lang = $label; - $label = \reset($lang); - $description = \next($lang); + $label = reset($lang); + $description = next($lang); $property->setDescription($description); } @@ -1352,8 +1373,8 @@ protected function parseSingleProperty(PropertyInterface $property, array $propI case 'eval': $property->setExtra( - \array_merge( - (array) $property->getExtra(), + array_merge( + $property->getExtra(), (array) $value ) ); @@ -1361,8 +1382,8 @@ protected function parseSingleProperty(PropertyInterface $property, array $propI case 'reference': $property->setExtra( - \array_merge( - (array) $property->getExtra(), + array_merge( + $property->getExtra(), ['reference' => &$propInfo['reference']] ) ); @@ -1400,12 +1421,12 @@ private function parseWidgetPageTree(PropertyInterface $property, array $propInf $property ->setExtra( - \array_merge( + array_merge( [ - 'sourceName' => \explode('.', $propInfo['foreignKey'])[0], + 'sourceName' => explode('.', $propInfo['foreignKey'])[0], 'idProperty' => 'id' ], - (array) $property->getExtra() + $property->getExtra() ) ); } @@ -1425,7 +1446,7 @@ private function parseOrderProperty(PropertyInterface $property, PropertyInterfa 'fileTree' => 'fileTreeOrder', 'DcGeneralTreePicker' => 'treePickerOrder' ]; - if (false === \array_key_exists($property->getWidgetType(), $orderWidgets)) { + if (false === array_key_exists($property->getWidgetType(), $orderWidgets)) { return; } @@ -1457,7 +1478,7 @@ protected function parseProperties(ContainerInterface $container) } // Some extensions create invalid DCA information and the DCA must therefore be validated. - if (!\is_array($propInfo)) { + if (!is_array($propInfo)) { continue; } @@ -1466,7 +1487,7 @@ protected function parseProperties(ContainerInterface $container) $extra = $property->getExtra(); if ( isset($extra['orderField']) - && \array_key_exists($extra['orderField'], (array) $this->getFromDca('fields')) + && array_key_exists($extra['orderField'], (array) $this->getFromDca('fields')) ) { if (!$definition->hasProperty($extra['orderField'])) { $definition->addProperty(new DefaultProperty($extra['orderField'])); @@ -1540,7 +1561,7 @@ protected function createCommand($commandName, array $commandDca) $parameters = $command->getParameters(); if (isset($commandDca['href'])) { - \parse_str($commandDca['href'], $queryParameters); + parse_str($commandDca['href'], $queryParameters); foreach ($queryParameters as $name => $value) { $parameters[$name] = $value; } @@ -1557,9 +1578,9 @@ protected function createCommand($commandName, array $commandDca) if (isset($commandDca['label'])) { $lang = $commandDca['label']; - if (\is_array($lang)) { - $label = \reset($lang); - $description = \next($lang); + if (is_array($lang)) { + $label = reset($lang); + $description = next($lang); $command->setDescription($description); } else { @@ -1580,7 +1601,7 @@ protected function createCommand($commandName, array $commandDca) // Callback is transformed into event in parseCallbacks(). unset($commandDca['button_callback']); - if (\count($commandDca)) { + if (count($commandDca)) { $extra = $command->getExtra(); foreach ($commandDca as $name => $value) { From 81233e406e767259a16b06b6f465e0c209fa8f69 Mon Sep 17 00:00:00 2001 From: Christian Schiffler Date: Wed, 18 Jan 2023 16:31:53 +0100 Subject: [PATCH 33/53] Fix psalm issues in Contao Dca namespace --- .../Dca/ContaoDataProviderInformation.php | 14 +- src/Contao/Dca/Definition/ExtendedDca.php | 16 +- .../Dca/Palette/LegacyPalettesParser.php | 153 ++++++++++-------- .../Dca/Populator/BackendViewPopulator.php | 11 +- .../Dca/Populator/DataProviderPopulator.php | 7 +- .../Populator/ExtendedLegacyDcaPopulator.php | 13 +- .../Dca/Populator/HardCodedPopulator.php | 10 +- .../Populator/ParentDefinitionPopulator.php | 17 +- 8 files changed, 143 insertions(+), 98 deletions(-) diff --git a/src/Contao/Dca/ContaoDataProviderInformation.php b/src/Contao/Dca/ContaoDataProviderInformation.php index 1e197a3d..c2fbfd71 100644 --- a/src/Contao/Dca/ContaoDataProviderInformation.php +++ b/src/Contao/Dca/ContaoDataProviderInformation.php @@ -34,14 +34,14 @@ class ContaoDataProviderInformation extends DataProviderInformation /** * The table name to use. * - * @var string + * @var string|null */ - protected $tableName; + protected $tableName = null; /** * Name of the provider class to use. * - * @var string + * @var class-string */ protected $className = DefaultDataProvider::class; @@ -50,7 +50,7 @@ class ContaoDataProviderInformation extends DataProviderInformation * * @var mixed */ - protected $initializationData; + protected $initializationData = null; /** * Set the table name of the data provider. @@ -69,7 +69,7 @@ public function setTableName($tableName) /** * Retrieve the table name of the data provider. * - * @return string + * @return string|null */ public function getTableName() { @@ -79,7 +79,7 @@ public function getTableName() /** * Set the data provider class to use, defaults to 'ContaoCommunityAlliance\DcGeneral\Data\DefaultDataProvider'. * - * @param string $className The name of the data provider class to use. + * @param class-string $className The name of the data provider class to use. * * @return ContaoDataProviderInformation */ @@ -93,7 +93,7 @@ public function setClassName($className) /** * Retrieve the data provider class to use. * - * @return string + * @return class-string */ public function getClassName() { diff --git a/src/Contao/Dca/Definition/ExtendedDca.php b/src/Contao/Dca/Definition/ExtendedDca.php index f0a7387c..6bcdf8b1 100644 --- a/src/Contao/Dca/Definition/ExtendedDca.php +++ b/src/Contao/Dca/Definition/ExtendedDca.php @@ -36,21 +36,21 @@ class ExtendedDca implements DefinitionInterface /** * Controller class to use. * - * @var string + * @var class-string|null */ - protected $controllerClass; + protected $controllerClass = null; /** * View class to use. * - * @var string + * @var class-string|null */ - protected $viewClass; + protected $viewClass = null; /** * Set the class name of the controller class. * - * @param string $controllerClass The class name. + * @param class-string $controllerClass The class name. * * @return void */ @@ -62,7 +62,7 @@ public function setControllerClass($controllerClass) /** * Get the class name of the controller class. * - * @return string + * @return class-string|null */ public function getControllerClass() { @@ -72,7 +72,7 @@ public function getControllerClass() /** * Set the class name of the view class. * - * @param string $viewClass The class name. + * @param class-string $viewClass The class name. * * @return void */ @@ -84,7 +84,7 @@ public function setViewClass($viewClass) /** * Get the class name of the view class. * - * @return string + * @return class-string|null */ public function getViewClass() { diff --git a/src/Contao/Dca/Palette/LegacyPalettesParser.php b/src/Contao/Dca/Palette/LegacyPalettesParser.php index 8046b651..3604f3ef 100644 --- a/src/Contao/Dca/Palette/LegacyPalettesParser.php +++ b/src/Contao/Dca/Palette/LegacyPalettesParser.php @@ -26,10 +26,10 @@ use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Condition\Palette\DefaultPaletteCondition; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Condition\Palette\PaletteConditionChain; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Condition\Palette\PaletteConditionInterface; -// @codingStandardsIgnoreStart -use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Condition\Palette\PropertyTrueCondition as PalettePropertyTrueCondition; -use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Condition\Palette\PropertyValueCondition as PalettePropertyValueCondition; -// @codingStandardsIgnoreEnd +use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Condition\Palette\PropertyTrueCondition + as PalettePropertyTrueCondition; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Condition\Palette\PropertyValueCondition + as PalettePropertyValueCondition; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Condition\Property\PropertyTrueCondition; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Condition\Property\PropertyValueCondition; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Legend; @@ -40,6 +40,21 @@ use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Property; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\PropertyInterface; +use function array_filter; +use function array_key_exists; +use function array_map; +use function array_merge; +use function array_shift; +use function array_unique; +use function count; +use function explode; +use function implode; +use function in_array; +use function is_string; +use function str_replace; +use function strpos; +use function substr_count; + /** * Class LegacyPalettesParser. * @@ -47,26 +62,24 @@ * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) + * + * @psalm-type TPalettesArray=array{default: string, __selector__?: list} */ class LegacyPalettesParser { /** * Parse the palette and sub palette array and create a complete palette collection. * - * @param array(string => string) $palettes The palettes from the DCA. - * @param array(string => string) $subPalettes The sub palettes from the DCA [optional]. + * @param TPalettesArray $palettes The palettes from the DCA. + * @param array $subPalettes The sub palettes from the DCA [optional]. * @param PaletteCollectionInterface|null $collection The palette collection to populate [optional]. * * @return PaletteCollectionInterface */ public function parse(array $palettes, array $subPalettes = [], PaletteCollectionInterface $collection = null) { - if (isset($palettes['__selector__'])) { - $selectorFieldNames = $palettes['__selector__']; - unset($palettes['__selector__']); - } else { - $selectorFieldNames = []; - } + $selectorFieldNames = $palettes['__selector__'] ?? []; + unset($palettes['__selector__']); return $this->parsePalettes( $palettes, @@ -79,12 +92,14 @@ public function parse(array $palettes, array $subPalettes = [], PaletteCollectio /** * Parse the given palettes. * - * @param array(string => string) $palettes The array of palettes, e.g. - * array('default' => '{title_legend},title'). - * @param array(string => string) $subPaletteProperties Mapped array from subpalette [optional]. - * @param array $selectorFieldNames List of names of the properties to be used as selector - * [optional]. - * @param PaletteCollectionInterface $collection The palette collection to populate [optional]. + * @param TPalettesArray $palettes The array of palettes, e.g. + * + * ['default' => '{title_legend},title'] + * . + * @param array> $subPaletteProperties Mapped array from subpalette [optional]. + * @param list $selectorFieldNames List of names of the properties to be used as + * selector [optional]. + * @param PaletteCollectionInterface|null $collection The collection to populate [optional]. * * @return PaletteCollectionInterface */ @@ -92,21 +107,22 @@ public function parsePalettes( array $palettes, array $subPaletteProperties = [], array $selectorFieldNames = [], - PaletteCollectionInterface $collection = null + ?PaletteCollectionInterface $collection = null ) { if (!$collection) { $collection = new PaletteCollection(); } if (isset($palettes['__selector__'])) { - $selectorFieldNames = \array_merge($selectorFieldNames, $palettes['__selector__']); - $selectorFieldNames = \array_unique($selectorFieldNames); - unset($palettes['__selector__']); + $selectorFieldNames = array_merge($selectorFieldNames, $palettes['__selector__']); + $selectorFieldNames = array_values(array_unique($selectorFieldNames)); } + unset($palettes['__selector__']); foreach ($palettes as $selector => $fields) { // Fields list must be a string. - if (!\is_string($fields)) { + /** @psalm-suppress DocblockTypeContradiction - only a contradiction when strict types are active */ + if (!is_string($fields)) { continue; } @@ -138,14 +154,14 @@ public function parsePalettes( /** * Parse a single palette. * - * @param string $paletteSelector The selector for the palette. - * @param string $fields The fields contained within the palette. - * @param array(string => PropertyInterface) $subPaletteProperties The sub palette properties [optional]. - * @param array(string) $selectorFieldNames The names of all properties being used as - * selectors [optional]. - * @param PaletteInterface $palette The palette to be populated [optional]. + * @param string $paletteSelector The selector for the palette. + * @param string $fields The fields contained within the palette. + * @param array> $subPaletteProperties The sub palette properties [optional]. + * @param list $selectorFieldNames The names of all properties being used as + * selectors [optional]. + * @param PaletteInterface|null $palette The palette to be populated [optional]. * - * @return Palette + * @return PaletteInterface * * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ @@ -165,8 +181,8 @@ public function parsePalette( // We ignore the difference between field set (separated by ";") and fields (separated by ","). $fields = preg_split('~[;,]~', $fields); - $fields = \array_map('trim', $fields); - $fields = \array_filter($fields); + $fields = array_map('trim', $fields); + $fields = array_filter($fields); $legend = null; @@ -179,7 +195,7 @@ public function parsePalette( $legend = new Legend($matches[1]); $palette->addLegend($legend); } - if (\array_key_exists(3, $matches)) { + if (array_key_exists(3, $matches)) { $legend->setInitialVisibility(false); } @@ -228,9 +244,9 @@ public function createPaletteCondition($paletteSelector, array $selectorFieldNam } // Legacy fallback, try to split on $selectors with optimistic suggestion of values. - if (false === \strpos($paletteSelector, '|')) { + if (false === strpos($paletteSelector, '|')) { foreach ($selectorFieldNames as $selectorFieldName) { - $paletteSelector = \str_replace( + $paletteSelector = str_replace( $selectorFieldName, '|' . $selectorFieldName . '|', $paletteSelector @@ -239,15 +255,15 @@ public function createPaletteCondition($paletteSelector, array $selectorFieldNam } // Extended mode, split selectors and values with "|". - $paletteSelectorParts = \explode('|', $paletteSelector); - $paletteSelectorParts = \array_map('trim', $paletteSelectorParts); - $paletteSelectorParts = \array_filter($paletteSelectorParts); + $paletteSelectorParts = explode('|', $paletteSelector); + $paletteSelectorParts = array_map('trim', $paletteSelectorParts); + $paletteSelectorParts = array_filter($paletteSelectorParts); $condition = new PaletteConditionChain(); foreach ($paletteSelectorParts as $paletteSelectorPart) { // The part is a property name (checkbox like selector). - if (\in_array($paletteSelectorPart, $selectorFieldNames)) { + if (in_array($paletteSelectorPart, $selectorFieldNames)) { $condition->addCondition( new PalettePropertyTrueCondition($paletteSelectorPart) ); @@ -276,17 +292,18 @@ public function createPaletteCondition($paletteSelector, array $selectorFieldNam /** * Parse the sub palettes and return the properties for each selector property. * - * @param array $subpalettes The sub palettes to parse. - * @param array $selectorFieldNames Names of the selector properties [optional]. + * @param array $subpalettes The sub palettes to parse. + * @param list $selectorFieldNames Names of the selector properties [optional]. * - * @return array(string => PropertyInterface[]) + * @return array> */ public function parseSubpalettes(array $subpalettes, array $selectorFieldNames = []) { $properties = []; foreach ($subpalettes as $subPaletteSelector => $childFields) { // Child fields list must be a string. - if (!\is_string($childFields)) { + /** @psalm-suppress DocblockTypeContradiction - only a contradiction when strict types are active */ + if (!is_string($childFields)) { continue; } @@ -296,7 +313,7 @@ public function parseSubpalettes(array $subpalettes, array $selectorFieldNames = // For selectable sub selector. if ( isset($properties[$selectorFieldName]) - && (0 < \substr_count($subPaletteSelector, '_')) + && (0 < substr_count($subPaletteSelector, '_')) ) { $selectorProperty = $properties[$selectorFieldName]; } @@ -315,12 +332,12 @@ public function parseSubpalettes(array $subpalettes, array $selectorFieldNames = /** * Parse the list of sub palette fields into an array of properties. * - * @param string $subPaletteSelector The selector in use. - * @param string $childFields List of the properties for the sub palette. - * @param array $selectorFieldNames List of the selector properties [optional]. - * @param array $properties List of the selector visible properties [optional]. + * @param string $subPaletteSelector The selector in use. + * @param string $childFields List of the properties for the sub palette. + * @param list $selectorFieldNames List of the selector properties [optional]. + * @param list $properties List of the selector visible properties [optional]. * - * @return PropertyInterface[] + * @return list */ public function parseSubpalette( $subPaletteSelector, @@ -328,8 +345,8 @@ public function parseSubpalette( array $selectorFieldNames = [], array $properties = [] ) { - $childFields = \explode(',', $childFields); - $childFields = \array_map('trim', $childFields); + $childFields = explode(',', $childFields); + $childFields = array_map('trim', $childFields); $condition = $this->createSubpaletteCondition($subPaletteSelector, $selectorFieldNames); @@ -354,22 +371,22 @@ public function parseSubpalette( * Case 2: the sub palette selector is only a "property name", the value is then implicated to be true. * In this cases a checkbox sub palette is in place. * - * @param string $subPaletteSelector The selector being evaluated. - * @param array $selectorFieldNames The names of the properties to be used as selectors [optional]. + * @param string $subPaletteSelector The selector being evaluated. + * @param list $selectorFieldNames The names of the properties to be used as selectors [optional]. * * @return string */ public function createSubpaletteSelectorFieldName($subPaletteSelector, array $selectorFieldNames = []) { - $selectorValues = \explode('_', $subPaletteSelector); - $selectorFieldName = \array_shift($selectorValues); - $selectorValueCount = \count($selectorValues); + $selectorValues = explode('_', $subPaletteSelector); + $selectorFieldName = array_shift($selectorValues); + $selectorValueCount = count($selectorValues); while ($selectorValueCount) { - if (\in_array($selectorFieldName, $selectorFieldNames)) { + if (in_array($selectorFieldName, $selectorFieldNames)) { break; } - $selectorFieldName .= '_' . \array_shift($selectorValues); - $selectorValueCount = \count($selectorValues); + $selectorFieldName .= '_' . array_shift($selectorValues); + $selectorValueCount = count($selectorValues); } return $selectorFieldName; @@ -387,30 +404,30 @@ public function createSubpaletteSelectorFieldName($subPaletteSelector, array $se * Case 2: the sub palette selector is only a "property name", the value is then implicated to be true. * In this cases a checkbox sub palette is in place. * - * @param string $subPaletteSelector The selector being evaluated. - * @param array $selectorFieldNames The names of the properties to be used as selectors [optional]. + * @param string $subPaletteSelector The selector being evaluated. + * @param list $selectorFieldNames The names of the properties to be used as selectors [optional]. * - * @return PropertyTrueCondition|PropertyValueCondition|null + * @return PropertyTrueCondition|PropertyValueCondition */ public function createSubpaletteCondition($subPaletteSelector, array $selectorFieldNames = []) { $condition = null; // Try to find a select value first (case 1). - $selectorValues = \explode('_', $subPaletteSelector); - $selectorFieldName = \array_shift($selectorValues); - $selectorValueCount = \count($selectorValues); + $selectorValues = explode('_', $subPaletteSelector); + $selectorFieldName = array_shift($selectorValues); + $selectorValueCount = count($selectorValues); while ($selectorValueCount) { if (empty($selectorValues)) { break; } - if (\in_array($selectorFieldName, $selectorFieldNames)) { - $condition = new PropertyValueCondition($selectorFieldName, \implode('_', $selectorValues)); + if (in_array($selectorFieldName, $selectorFieldNames)) { + $condition = new PropertyValueCondition($selectorFieldName, implode('_', $selectorValues)); break; } - $selectorFieldName .= '_' . \array_shift($selectorValues); + $selectorFieldName .= '_' . array_shift($selectorValues); } // If case 1 was not successful, try implicitly case 2 must apply. diff --git a/src/Contao/Dca/Populator/BackendViewPopulator.php b/src/Contao/Dca/Populator/BackendViewPopulator.php index bb6fc24e..0249476b 100644 --- a/src/Contao/Dca/Populator/BackendViewPopulator.php +++ b/src/Contao/Dca/Populator/BackendViewPopulator.php @@ -37,6 +37,8 @@ /** * This class is the default fallback populator in the Contao Backend to instantiate a BackendView. + * + * @psalm-suppress PropertyNotSetInConstructor - can not make setScopeDeterminator() final without major release. */ class BackendViewPopulator extends AbstractEventDrivenBackendEnvironmentPopulator { @@ -72,7 +74,7 @@ protected function populateView(EnvironmentInterface $environment) $dataDefinition = $environment->getDataDefinition(); - if (!$dataDefinition->hasBasicDefinition()) { + if (!$dataDefinition || !$dataDefinition->hasBasicDefinition()) { return; } @@ -88,7 +90,9 @@ protected function populateView(EnvironmentInterface $environment) break; default: $mode = $dataDefinition->getBasicDefinition()->getMode(); - throw new DcGeneralInvalidArgumentException('Unknown view mode encountered: ' . $mode); + throw new DcGeneralInvalidArgumentException( + 'Unknown view mode encountered: ' . var_export($mode, true) + ); } $view->setEnvironment($environment); @@ -114,7 +118,8 @@ protected function populatePanel(EnvironmentInterface $environment) return; } - if (!$environment->getDataDefinition()->hasDefinition(Contao2BackendViewDefinitionInterface::NAME)) { + $definition = $environment->getDataDefinition(); + if (!$definition || !$definition->hasDefinition(Contao2BackendViewDefinitionInterface::NAME)) { return; } diff --git a/src/Contao/Dca/Populator/DataProviderPopulator.php b/src/Contao/Dca/Populator/DataProviderPopulator.php index a0b3200a..d10a3568 100644 --- a/src/Contao/Dca/Populator/DataProviderPopulator.php +++ b/src/Contao/Dca/Populator/DataProviderPopulator.php @@ -41,7 +41,7 @@ class DataProviderPopulator extends AbstractEventDrivenEnvironmentPopulator /** * The cached instances of the data provider. * - * @var DataProviderInterface[] + * @var array */ private $instances = []; @@ -71,7 +71,10 @@ public function processEvent(PopulateEnvironmentEvent $event): void */ public function populate(EnvironmentInterface $environment) { - foreach ($environment->getDataDefinition()->getDataProviderDefinition() as $information) { + if (null === $definition = $environment->getDataDefinition()) { + return; + } + foreach ($definition->getDataProviderDefinition() as $information) { if ($information instanceof ContaoDataProviderInformation) { if ($environment->hasDataProvider($information->getName())) { throw new DcGeneralRuntimeException( diff --git a/src/Contao/Dca/Populator/ExtendedLegacyDcaPopulator.php b/src/Contao/Dca/Populator/ExtendedLegacyDcaPopulator.php index cd6ea606..6e5555ae 100644 --- a/src/Contao/Dca/Populator/ExtendedLegacyDcaPopulator.php +++ b/src/Contao/Dca/Populator/ExtendedLegacyDcaPopulator.php @@ -33,6 +33,8 @@ * * This class only populates the environment with the extended information available via the ExtendedDca data definition * section. + * + * @psalm-suppress PropertyNotSetInConstructor - can not make setScopeDeterminator() final without major release. */ class ExtendedLegacyDcaPopulator extends AbstractEventDrivenBackendEnvironmentPopulator { @@ -67,12 +69,13 @@ protected function populateView(EnvironmentInterface $environment) $definition = $environment->getDataDefinition(); // If we encounter an extended definition, that one may override. - if (!$definition->hasDefinition(ExtendedDca::NAME)) { + if (!$definition || !$definition->hasDefinition(ExtendedDca::NAME)) { return; } /** @var ExtendedDca $extendedDefinition */ - if (!($class = $definition->getDefinition(ExtendedDca::NAME)->getViewClass())) { + $extendedDefinition = $definition->getDefinition(ExtendedDca::NAME); + if (!($class = $extendedDefinition->getViewClass())) { return; } @@ -101,11 +104,13 @@ public function populateController(EnvironmentInterface $environment) $definition = $environment->getDataDefinition(); // If we encounter an extended definition, that one may override. - if (!$definition->hasDefinition(ExtendedDca::NAME)) { + if (!$definition || !$definition->hasDefinition(ExtendedDca::NAME)) { return; } - if (!($class = $definition->getDefinition(ExtendedDca::NAME)->getControllerClass())) { + /** @var ExtendedDca $extendedDefinition */ + $extendedDefinition = $definition->getDefinition(ExtendedDca::NAME); + if (!($class = $extendedDefinition->getControllerClass())) { return; } diff --git a/src/Contao/Dca/Populator/HardCodedPopulator.php b/src/Contao/Dca/Populator/HardCodedPopulator.php index 13c5937f..8ce72df1 100644 --- a/src/Contao/Dca/Populator/HardCodedPopulator.php +++ b/src/Contao/Dca/Populator/HardCodedPopulator.php @@ -24,8 +24,10 @@ use Contao\System; use ContaoCommunityAlliance\DcGeneral\BaseConfigRegistry; use ContaoCommunityAlliance\DcGeneral\Clipboard\Clipboard; +use ContaoCommunityAlliance\DcGeneral\Contao\Factory\SessionStorageFactory; use ContaoCommunityAlliance\DcGeneral\Contao\InputProvider; use ContaoCommunityAlliance\DcGeneral\Controller\DefaultController; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; use ContaoCommunityAlliance\DcGeneral\EnvironmentPopulator\AbstractEventDrivenEnvironmentPopulator; @@ -69,8 +71,12 @@ public function populateController(EnvironmentInterface $environment) public function populate(EnvironmentInterface $environment) { if (!$environment->getSessionStorage()) { - $sessionStorage = System::getContainer()->get('cca.dc-general.session_factory')->createService(); - $sessionStorage->setScope('DC_GENERAL_' . \strtoupper($environment->getDataDefinition()->getName())); + /** @var SessionStorageFactory $sessionFactory */ + $sessionFactory = System::getContainer()->get('cca.dc-general.session_factory'); + $definition = $environment->getDataDefinition(); + $sessionStorage = $sessionFactory->createService(); + assert($definition instanceof ContainerInterface); + $sessionStorage->setScope('DC_GENERAL_' . \strtoupper($definition->getName())); $environment->setSessionStorage($sessionStorage); // @codingStandardsIgnoreStart @\trigger_error('Fallback populator in use - implement a proper populator!', E_USER_DEPRECATED); diff --git a/src/Contao/Dca/Populator/ParentDefinitionPopulator.php b/src/Contao/Dca/Populator/ParentDefinitionPopulator.php index 3911e332..ffcc2eac 100644 --- a/src/Contao/Dca/Populator/ParentDefinitionPopulator.php +++ b/src/Contao/Dca/Populator/ParentDefinitionPopulator.php @@ -19,9 +19,11 @@ namespace ContaoCommunityAlliance\DcGeneral\Contao\Dca\Populator; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; use ContaoCommunityAlliance\DcGeneral\EnvironmentPopulator\AbstractEventDrivenEnvironmentPopulator; use ContaoCommunityAlliance\DcGeneral\Factory\DcGeneralFactory; +use LogicException; /** * Class ParentDefinitionPopulator. @@ -45,17 +47,24 @@ public function populateController(EnvironmentInterface $environment) { $definition = $environment->getDataDefinition(); - if (!$definition->getBasicDefinition()->getParentDataProvider()) { + if (!$definition || !($parentDataProvider = $definition->getBasicDefinition()->getParentDataProvider())) { return; } + if (null === $dispatcher = $environment->getEventDispatcher()) { + throw new LogicException('No event dispatcher given'); + } + if (null === $translator = $environment->getTranslator()) { + throw new LogicException('No translator given'); + } $parentDefinition = (new DcGeneralFactory()) - ->setEventDispatcher($environment->getEventDispatcher()) - ->setTranslator($environment->getTranslator()) - ->setContainerName($definition->getBasicDefinition()->getParentDataProvider()) + ->setEventDispatcher($dispatcher) + ->setTranslator($translator) + ->setContainerName($parentDataProvider) ->createDcGeneral() ->getEnvironment() ->getDataDefinition(); + assert($parentDefinition instanceof ContainerInterface); $environment->setParentDataDefinition($parentDefinition); } From a2f03e5d97cc6f21097da03202943184a8a2de7c Mon Sep 17 00:00:00 2001 From: Christian Schiffler Date: Wed, 18 Jan 2023 16:32:04 +0100 Subject: [PATCH 34/53] Fix exception message --- src/Contao/Cache/Http/DeleteModelInvalidateCacheTags.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Contao/Cache/Http/DeleteModelInvalidateCacheTags.php b/src/Contao/Cache/Http/DeleteModelInvalidateCacheTags.php index 40457379..3fda8ba1 100644 --- a/src/Contao/Cache/Http/DeleteModelInvalidateCacheTags.php +++ b/src/Contao/Cache/Http/DeleteModelInvalidateCacheTags.php @@ -49,7 +49,7 @@ protected function getEnvironment(AbstractModelAwareEvent $event): EnvironmentIn throw new LogicException('No event dispatcher given'); } if (null === $translator = $environment->getTranslator()) { - throw new LogicException('No event translator given'); + throw new LogicException('No translator given'); } return $this->factory ->createFactory() From 990949f42d4b06e9d7be44c83069fb72c4689715 Mon Sep 17 00:00:00 2001 From: Christian Schiffler Date: Wed, 18 Jan 2023 16:32:27 +0100 Subject: [PATCH 35/53] Fix type casts --- src/BaseConfigRegistry.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/BaseConfigRegistry.php b/src/BaseConfigRegistry.php index 71656c76..2ba062da 100644 --- a/src/BaseConfigRegistry.php +++ b/src/BaseConfigRegistry.php @@ -100,7 +100,7 @@ private function addParentFilter(ModelIdInterface $idParent, ConfigInterface $co if ($basicDefinition->getParentDataProvider() !== $parentProviderName) { throw new DcGeneralRuntimeException( 'Unexpected parent provider ' . $parentProviderName . - ' (expected ' . $basicDefinition->getParentDataProvider() . ')' + ' (expected ' . ((string) $basicDefinition->getParentDataProvider()) . ')' ); } @@ -114,7 +114,7 @@ private function addParentFilter(ModelIdInterface $idParent, ConfigInterface $co $condition = $definition->getModelRelationshipDefinition()->getChildCondition( $parentProviderName, - $providerName + (string) $providerName ); if ($condition) { From 78d780ac713dbd4209fd80d061e3c552bd05b9fa Mon Sep 17 00:00:00 2001 From: Christian Schiffler Date: Wed, 18 Jan 2023 17:00:17 +0100 Subject: [PATCH 36/53] Fix even more psalm issues --- src/Contao/Compatibility/ActiveRecord.php | 12 ++++++++++-- src/Contao/Compatibility/DcCompat.php | 2 +- src/Contao/Event/Subscriber.php | 13 ++++++++----- .../Contao2BackendView/BackendViewInterface.php | 2 +- src/Contao/View/Contao2BackendView/BaseView.php | 8 ++++---- .../Event/GetPanelElementTemplateEvent.php | 4 ++-- .../Event/GetPropertyOptionsEvent.php | 16 ++++++++-------- src/DataDefinition/ContainerInterface.php | 2 +- .../Definition/BasicDefinitionInterface.php | 12 ++++++------ .../Definition/DefaultBasicDefinition.php | 14 +++++++------- ...efaultGroupAndSortingDefinitionCollection.php | 2 +- .../Definition/View/DefaultListingConfig.php | 14 +++++++------- ...upAndSortingDefinitionCollectionInterface.php | 2 +- .../View/GroupAndSortingDefinitionInterface.php | 6 +++++- .../Definition/View/ListingConfigInterface.php | 10 +++++----- src/View/ViewInterface.php | 2 +- 16 files changed, 68 insertions(+), 53 deletions(-) diff --git a/src/Contao/Compatibility/ActiveRecord.php b/src/Contao/Compatibility/ActiveRecord.php index 7ab74493..1c09d831 100644 --- a/src/Contao/Compatibility/ActiveRecord.php +++ b/src/Contao/Compatibility/ActiveRecord.php @@ -46,7 +46,11 @@ public function __construct(ModelInterface $model) } /** - * {@inheritdoc} + * Magic getter. + * + * @param string $name The name of the property to get. + * + * @return mixed */ public function __get($name) { @@ -54,7 +58,11 @@ public function __get($name) } /** - * {@inheritdoc} + * Magic setter. + * + * @param string $name The name of the property to set. + * @param mixed $value The value of the property to set. + * */ public function __set($name, $value) { diff --git a/src/Contao/Compatibility/DcCompat.php b/src/Contao/Compatibility/DcCompat.php index c36b4e8d..a1d08c0f 100644 --- a/src/Contao/Compatibility/DcCompat.php +++ b/src/Contao/Compatibility/DcCompat.php @@ -53,7 +53,7 @@ class DcCompat extends General * * @param EnvironmentInterface $environment The Dc instance to use for delegating. * @param ModelInterface $model The model within scope (optional). - * @param null $propertyName The name of the property within scope (optional). + * @param string|null $propertyName The name of the property within scope (optional). */ public function __construct(EnvironmentInterface $environment, ModelInterface $model = null, $propertyName = null) { diff --git a/src/Contao/Event/Subscriber.php b/src/Contao/Event/Subscriber.php index 07822419..da822b0b 100644 --- a/src/Contao/Event/Subscriber.php +++ b/src/Contao/Event/Subscriber.php @@ -66,7 +66,7 @@ class Subscriber implements EventSubscriberInterface * * @var RequestScopeDeterminator */ - private $scopeDeterminator; + private RequestScopeDeterminator $scopeDeterminator; /** * ClipboardController constructor. @@ -81,9 +81,9 @@ public function __construct(RequestScopeDeterminator $scopeDeterminator) /** * The config instance. * - * @var \Contao\Config + * @var \Contao\Config|null */ - private static $config; + private static $config = null; /** * {@inheritDoc} @@ -166,15 +166,18 @@ public function resolveWidgetErrorMessage(ResolveWidgetErrorMessageEvent $event) * @param ModelInterface $model The model. * @param PropertyInterface $property The property. * - * @return array + * @return array|null */ protected static function getOptions($environment, $model, $property) { + if (null === $dispatcher = $environment->getEventDispatcher()) { + return $property->getOptions(); + } $event = new GetPropertyOptionsEvent($environment, $model); $event->setPropertyName($property->getName()); $event->setOptions($property->getOptions()); - $environment->getEventDispatcher()->dispatch($event, $event::NAME); + $dispatcher->dispatch($event, $event::NAME); return $event->getOptions(); } diff --git a/src/Contao/View/Contao2BackendView/BackendViewInterface.php b/src/Contao/View/Contao2BackendView/BackendViewInterface.php index 92c7b6b8..5875080c 100644 --- a/src/Contao/View/Contao2BackendView/BackendViewInterface.php +++ b/src/Contao/View/Contao2BackendView/BackendViewInterface.php @@ -43,7 +43,7 @@ public function setPanel($panelContainer); /** * Retrieve the panel container from the view. * - * @return PanelContainerInterface + * @return PanelContainerInterface|null */ public function getPanel(); } diff --git a/src/Contao/View/Contao2BackendView/BaseView.php b/src/Contao/View/Contao2BackendView/BaseView.php index 919cb76e..e48c9c50 100644 --- a/src/Contao/View/Contao2BackendView/BaseView.php +++ b/src/Contao/View/Contao2BackendView/BaseView.php @@ -83,16 +83,16 @@ public function __construct(RequestScopeDeterminator $scopeDeterminator) /** * The attached environment. * - * @var EnvironmentInterface + * @var EnvironmentInterface|null */ - protected $environment; + protected $environment = null; /** * The panel container in use. * - * @var PanelContainerInterface + * @var PanelContainerInterface|null */ - protected $panel; + protected $panel = null; /** * {@inheritDoc} diff --git a/src/Contao/View/Contao2BackendView/Event/GetPanelElementTemplateEvent.php b/src/Contao/View/Contao2BackendView/Event/GetPanelElementTemplateEvent.php index c7c90c1b..4135ea48 100644 --- a/src/Contao/View/Contao2BackendView/Event/GetPanelElementTemplateEvent.php +++ b/src/Contao/View/Contao2BackendView/Event/GetPanelElementTemplateEvent.php @@ -45,7 +45,7 @@ class GetPanelElementTemplateEvent extends AbstractEnvironmentAwareEvent /** * The template instance. * - * @var ViewTemplateInterface + * @var ViewTemplateInterface|null */ protected $template; @@ -74,7 +74,7 @@ public function getElement() /** * Retrieve the template instance. * - * @return ViewTemplateInterface + * @return ViewTemplateInterface|null */ public function getTemplate() { diff --git a/src/Contao/View/Contao2BackendView/Event/GetPropertyOptionsEvent.php b/src/Contao/View/Contao2BackendView/Event/GetPropertyOptionsEvent.php index 890131a2..abe770e9 100644 --- a/src/Contao/View/Contao2BackendView/Event/GetPropertyOptionsEvent.php +++ b/src/Contao/View/Contao2BackendView/Event/GetPropertyOptionsEvent.php @@ -35,21 +35,21 @@ class GetPropertyOptionsEvent extends AbstractModelAwareEvent /** * The name of the property to retrieve the options for. * - * @var string + * @var string|null */ - protected $propertyName; + protected $propertyName = null; /** * The options for the properties. * - * @var array + * @var array|null */ - protected $options; + protected $options = null; /** * Set the property name to retrieve the options for. * - * @param string $propertyName The name of the property. + * @param string|null $propertyName The name of the property. * * @return $this */ @@ -63,7 +63,7 @@ public function setPropertyName($propertyName) /** * Get the property name to retrieve the options for. * - * @return string + * @return string|null */ public function getPropertyName() { @@ -73,7 +73,7 @@ public function getPropertyName() /** * Set the options for the property in the event. * - * @param array $options The options. + * @param array|null $options The options. * * @return $this */ @@ -87,7 +87,7 @@ public function setOptions($options) /** * Retrieve the options for the property from the event. * - * @return array + * @return array|null */ public function getOptions() { diff --git a/src/DataDefinition/ContainerInterface.php b/src/DataDefinition/ContainerInterface.php index a2602d47..ed126be9 100644 --- a/src/DataDefinition/ContainerInterface.php +++ b/src/DataDefinition/ContainerInterface.php @@ -117,7 +117,7 @@ public function getDefinitionNames(); /** * Convenience method to check if a basic definition is contained. * - * @return BasicDefinitionInterface + * @return bool */ public function hasBasicDefinition(); diff --git a/src/DataDefinition/Definition/BasicDefinitionInterface.php b/src/DataDefinition/Definition/BasicDefinitionInterface.php index d50ffbba..96db9770 100644 --- a/src/DataDefinition/Definition/BasicDefinitionInterface.php +++ b/src/DataDefinition/Definition/BasicDefinitionInterface.php @@ -62,7 +62,7 @@ public function setMode($mode); /** * Get the mode the data definition is in. * - * @return int + * @return int|null */ public function getMode(); @@ -88,7 +88,7 @@ public function setRootDataProvider($providerName); * * Note: This does only apply when in tree mode or parenting mode. For flat mode this does not make sense. * - * @return string + * @return string|null */ public function getRootDataProvider(); @@ -108,7 +108,7 @@ public function setParentDataProvider($providerName); * * Note: This does only apply when in tree mode or parenting mode. For flat mode this does not make sense. * - * @return string + * @return string|null */ public function getParentDataProvider(); @@ -124,7 +124,7 @@ public function setDataProvider($providerName); /** * Retrieve the name of data provider which holds the models that we work on. * - * @return string + * @return string|null */ public function getDataProvider(); @@ -152,7 +152,7 @@ public function hasAdditionalFilter($dataProvider = null); * * @param string $dataProvider The name of the data provider for which additional filters shall be retrieved. * - * @return array + * @return array|null */ public function getAdditionalFilter($dataProvider = null); @@ -267,7 +267,7 @@ public function setRootEntries($entries); /** * Get the ids of the root elements (only valid when in hierarchical mode). * - * @return mixed[] + * @return mixed[]|null */ public function getRootEntries(); diff --git a/src/DataDefinition/Definition/DefaultBasicDefinition.php b/src/DataDefinition/Definition/DefaultBasicDefinition.php index e4e3104f..66e84858 100644 --- a/src/DataDefinition/Definition/DefaultBasicDefinition.php +++ b/src/DataDefinition/Definition/DefaultBasicDefinition.php @@ -31,35 +31,35 @@ class DefaultBasicDefinition implements BasicDefinitionInterface /** * The mode. * - * @var int + * @var int|null */ protected $mode; /** * The name of the data provider of the root elements. * - * @var string + * @var string|null */ protected $rootProviderName; /** * The name of the data provider of the parent element. * - * @var string + * @var string|null */ protected $parentProviderName; /** * The name of the data provider of the elements being processed. * - * @var string + * @var string|null */ protected $providerName; /** * Array of filter rules. * - * @var array + * @var array|null */ protected $additionalFilter; @@ -94,14 +94,14 @@ class DefaultBasicDefinition implements BasicDefinitionInterface /** * Determines if the view shall switch automatically into edit mode. * - * @var bool + * @var bool|null */ protected $switchToEditEnabled; /** * The ids of the root entries. * - * @var mixed[] + * @var mixed[]|null */ protected $rootEntries = []; diff --git a/src/DataDefinition/Definition/View/DefaultGroupAndSortingDefinitionCollection.php b/src/DataDefinition/Definition/View/DefaultGroupAndSortingDefinitionCollection.php index 0536c25e..61598f88 100644 --- a/src/DataDefinition/Definition/View/DefaultGroupAndSortingDefinitionCollection.php +++ b/src/DataDefinition/Definition/View/DefaultGroupAndSortingDefinitionCollection.php @@ -32,7 +32,7 @@ class DefaultGroupAndSortingDefinitionCollection implements GroupAndSortingDefin /** * The information stored. * - * @var GroupAndSortingDefinitionInterface[] + * @var list */ protected $information = []; diff --git a/src/DataDefinition/Definition/View/DefaultListingConfig.php b/src/DataDefinition/Definition/View/DefaultListingConfig.php index 2a3e7329..cfc5b375 100644 --- a/src/DataDefinition/Definition/View/DefaultListingConfig.php +++ b/src/DataDefinition/Definition/View/DefaultListingConfig.php @@ -42,28 +42,28 @@ class DefaultListingConfig implements ListingConfigInterface /** * The properties to display in the heder (parented mode only). * - * @var array + * @var array|null */ protected $headerProperties; /** * The root icon to use (hierarchical mode only). * - * @var string + * @var string|null */ protected $rootIcon; /** * The root label. * - * @var string + * @var string|null */ protected $rootLabel; /** * The CSS class to apply to each item in the listing. * - * @var string + * @var string|null */ protected $itemCssClass; @@ -72,19 +72,19 @@ class DefaultListingConfig implements ListingConfigInterface * * @var DefaultModelFormatterConfig[] */ - protected $itemFormatter; + protected $itemFormatter = []; /** * Flag if the properties displayed shall be shown as table layout. * - * @var bool + * @var bool|null */ protected $showColumns; /** * The parent table property name. * - * @var string + * @var string|null */ protected $parentTablePropertyName; diff --git a/src/DataDefinition/Definition/View/GroupAndSortingDefinitionCollectionInterface.php b/src/DataDefinition/Definition/View/GroupAndSortingDefinitionCollectionInterface.php index 83512dd4..1e5a3a0d 100644 --- a/src/DataDefinition/Definition/View/GroupAndSortingDefinitionCollectionInterface.php +++ b/src/DataDefinition/Definition/View/GroupAndSortingDefinitionCollectionInterface.php @@ -88,7 +88,7 @@ public function hasDefault(); /** * Retrieve the default definition. * - * @return GroupAndSortingDefinitionInterface|GroupAndSortingInformationInterface[] + * @return GroupAndSortingDefinitionInterface */ public function getDefault(); diff --git a/src/DataDefinition/Definition/View/GroupAndSortingDefinitionInterface.php b/src/DataDefinition/Definition/View/GroupAndSortingDefinitionInterface.php index ca4c65a5..9e0d0501 100644 --- a/src/DataDefinition/Definition/View/GroupAndSortingDefinitionInterface.php +++ b/src/DataDefinition/Definition/View/GroupAndSortingDefinitionInterface.php @@ -20,10 +20,14 @@ namespace ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View; +use IteratorAggregate; + /** * This interface defines a grouping and sorting information for the view. + * + * @extends IteratorAggregate */ -interface GroupAndSortingDefinitionInterface extends \IteratorAggregate +interface GroupAndSortingDefinitionInterface extends IteratorAggregate { /** * Add a new information - optionally at the given position. diff --git a/src/DataDefinition/Definition/View/ListingConfigInterface.php b/src/DataDefinition/Definition/View/ListingConfigInterface.php index 3c4afdc2..d7af4a9b 100644 --- a/src/DataDefinition/Definition/View/ListingConfigInterface.php +++ b/src/DataDefinition/Definition/View/ListingConfigInterface.php @@ -81,7 +81,7 @@ public function setRootIcon($value); /** * Return the icon path to the root item's icon. * - * @return string + * @return string|null */ public function getRootIcon(); @@ -97,7 +97,7 @@ public function setRootLabel($value); /** * Get the root label. * - * @return string + * @return string|null */ public function getRootLabel(); @@ -113,7 +113,7 @@ public function setItemCssClass($value); /** * Return css classes that should be added to the items container. * - * @return string + * @return string|null */ public function getItemCssClass(); @@ -157,7 +157,7 @@ public function setShowColumns($value); /** * Get if the listing shall be in table columns. * - * @return bool + * @return bool|null * * @SuppressWarnings(PHPMD.BooleanGetMethodName) */ @@ -166,7 +166,7 @@ public function getShowColumns(); /** * Get the parent table property name. * - * @return string + * @return string|null */ public function getParentTablePropertyName(); diff --git a/src/View/ViewInterface.php b/src/View/ViewInterface.php index ca229d57..fd8f145c 100644 --- a/src/View/ViewInterface.php +++ b/src/View/ViewInterface.php @@ -42,7 +42,7 @@ public function setEnvironment(EnvironmentInterface $environment); /** * Retrieve the attached environment. * - * @return EnvironmentInterface + * @return EnvironmentInterface|null */ public function getEnvironment(); From d63ccb8f9c40ef514d28e49bbfe4e541919315a2 Mon Sep 17 00:00:00 2001 From: Ingolf Steinhardt Date: Tue, 5 Sep 2023 21:46:06 +0200 Subject: [PATCH 37/53] Any fixes --- .composer-require-checker.json | 2 +- composer.json | 3 ++- src/Contao/Event/Subscriber.php | 32 +++++++++++--------------------- 3 files changed, 14 insertions(+), 23 deletions(-) diff --git a/.composer-require-checker.json b/.composer-require-checker.json index 448a6c93..7a3eff87 100644 --- a/.composer-require-checker.json +++ b/.composer-require-checker.json @@ -1,6 +1,6 @@ { "symbol-whitelist": [ - "array", "bool", "false", "int", "null", "self", "static", "parent", "string", "true", "void", + "array", "bool", "false", "int", "mixed", "null", "self", "static", "parent", "string", "true", "void", "ampersand", "array_insert", "array_is_assoc", "nl2br_html5", "TL_ERROR", "ContaoTwigInitializeEvent", "ContaoCommunityAlliance\\DcGeneral\\Contao\\View\\Contao2BackendView\\Exception\\EditOnlyModeException", diff --git a/composer.json b/composer.json index 4ba6bd1c..bb0eed9a 100644 --- a/composer.json +++ b/composer.json @@ -88,7 +88,8 @@ "config": { "allow-plugins": { "contao-components/installer": false, - "contao/manager-plugin": true + "contao/manager-plugin": true, + "php-http/discovery": true } }, "extra": { diff --git a/src/Contao/Event/Subscriber.php b/src/Contao/Event/Subscriber.php index 318a38b6..5ca21aca 100644 --- a/src/Contao/Event/Subscriber.php +++ b/src/Contao/Event/Subscriber.php @@ -84,7 +84,7 @@ public function __construct(RequestScopeDeterminator $scopeDeterminator) * * @var Config|null */ - private static Config $config = null; + private static ?Config $config = null; /** * {@inheritDoc} @@ -173,8 +173,7 @@ protected static function getOptions( EnvironmentInterface $environment, ModelInterface $model, PropertyInterface $property - ): array - { + ): array { if (null === $dispatcher = $environment->getEventDispatcher()) { return $property->getOptions(); } @@ -202,8 +201,7 @@ private static function decodeValue( ModelInterface $model, string $property, mixed $value - ): mixed - { + ): mixed { $event = new DecodePropertyValueForWidgetEvent($environment, $model); $event ->setProperty($property) @@ -227,8 +225,7 @@ private static function parseDateTime( EventDispatcherInterface $dispatcher, string $dateFormat, int $timeStamp - ): string - { + ): string { $dateEvent = new ParseDateEvent($timeStamp, $dateFormat); $dispatcher->dispatch($dateEvent, ContaoEvents::DATE_PARSE); @@ -395,8 +392,7 @@ private static function renderForeignKeyReadable( RenderReadablePropertyValueEvent $event, array $extra, mixed $value - ): void - { + ): void { if (!isset($extra['foreignKey']) || (null !== $event->getRendered())) { return; } @@ -441,8 +437,7 @@ private static function renderTimestampReadable( RenderReadablePropertyValueEvent $event, array $extra, int $value - ): void - { + ): void { if ( !isset($extra['rgxp']) || !(('date' === $extra['rgxp']) || ('time' === $extra['rgxp']) || ('datim' === $extra['rgxp'])) @@ -471,8 +466,7 @@ private static function renderDateTimePropertyIsTstamp( RenderReadablePropertyValueEvent $event, PropertyInterface $property, int $value - ): void - { + ): void { if ((null !== $event->getRendered()) || ('tstamp' !== $property->getName())) { return; } @@ -498,8 +492,7 @@ private static function renderSimpleCheckbox( PropertyInterface $property, array $extra, int $value - ): void - { + ): void { if ( (null !== $event->getRendered()) || !(!($extra['multiple'] ?? false) && ('checkbox' === $property->getWidgetType())) @@ -542,8 +535,7 @@ private static function renderReferenceReadable( RenderReadablePropertyValueEvent $event, array $extra, string $value - ): void - { + ): void { if ( !isset($extra['reference']) || !\array_key_exists($value, (array)$extra['reference']) @@ -576,8 +568,7 @@ private static function renderTextAreaReadable( PropertyInterface $property, array $extra, string $value - ): void - { + ): void { if ( (empty($extra['allowHtml']) && empty($extra['preserveTags'])) || (null !== $event->getRendered()) @@ -602,8 +593,7 @@ private static function renderOptionValueReadable( RenderReadablePropertyValueEvent $event, PropertyInterface $property, mixed $value - ): void - { + ): void { if (!($options = $property->getOptions())) { $options = self::getOptions($event->getEnvironment(), $event->getModel(), $event->getProperty()); if ($options) { From 2d24cb4db61679e39dd3ca0dec7db1e2c0f1dc0e Mon Sep 17 00:00:00 2001 From: Ingolf Steinhardt Date: Tue, 5 Sep 2023 23:05:47 +0200 Subject: [PATCH 38/53] Any fixes --- src/Contao/Event/Subscriber.php | 6 +- .../AbstractListShowAllHandler.php | 6 +- .../ActionHandler/ShowHandler.php | 1 + .../View/Contao2BackendView/FileSelect.php | 9 +- .../View/Contao2BackendView/PanelRenderer.php | 8 +- .../Subscriber/GetGroupHeaderSubscriber.php | 7 +- .../Subscriber/WidgetBuilder.php | 4 +- .../View/Contao2BackendView/TreeSelect.php | 9 +- src/Controller/BackendTreeController.php | 9 +- src/Controller/DefaultController.php | 9 +- src/Data/DefaultCollection.php | 16 +- src/Data/DefaultDataProvider.php | 2080 ++++++++--------- src/Data/DefaultFilterOptionCollection.php | 7 +- .../DefaultLanguageInformationCollection.php | 7 +- src/Data/PropertyValueBag.php | 8 +- .../DefaultDataProviderDefinition.php | 7 +- .../DefaultGroupAndSortingInformation.php | 7 +- .../FilterBuilderWithChildren.php | 21 +- src/Panel/DefaultPanel.php | 7 +- src/Panel/DefaultPanelContainer.php | 7 +- 20 files changed, 1126 insertions(+), 1109 deletions(-) diff --git a/src/Contao/Event/Subscriber.php b/src/Contao/Event/Subscriber.php index 5ca21aca..05dfe072 100644 --- a/src/Contao/Event/Subscriber.php +++ b/src/Contao/Event/Subscriber.php @@ -260,14 +260,14 @@ public function renderReadablePropertyValue(RenderReadablePropertyValueEvent $ev $extra = $property->getExtra(); switch (true) { - case (\is_string($value)); + case (\is_string($value)): self::renderTextAreaReadable($event, $property, $extra, $value); self::renderReferenceReadable($event, $extra, $value); break; - case (\is_array($value)); + case (\is_array($value)): self::renderArrayReadable($event, $value); break; - case (\is_int($value)); + case (\is_int($value)): self::renderTimestampReadable($event, $extra, $value); self::renderDateTimePropertyIsTstamp($event, $property, $value); self::renderSimpleCheckbox($event, $property, $extra, $value); diff --git a/src/Contao/View/Contao2BackendView/ActionHandler/AbstractListShowAllHandler.php b/src/Contao/View/Contao2BackendView/ActionHandler/AbstractListShowAllHandler.php index 67425720..bb0837bb 100644 --- a/src/Contao/View/Contao2BackendView/ActionHandler/AbstractListShowAllHandler.php +++ b/src/Contao/View/Contao2BackendView/ActionHandler/AbstractListShowAllHandler.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2022 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -16,7 +16,7 @@ * @author David Molineus * @author Richard Henkenjohann * @author Ingolf Steinhardt - * @copyright 2013-2022 Contao Community Alliance. + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -311,7 +311,7 @@ protected function renderTemplate(ContaoBackendViewTemplate $template, Environme ->set('subHeadline', $this->translate('MSC.select_models', 'contao_default')) ->set('tableName', ($definition->getName() ?? 'none')) ->set('select', 'select' === $environment->getInputProvider()->getParameter('act')) - ->set('action', \ampersand(Environment::get('request'))) + ->set('action', StringUtil::ampersand(Environment::get('request'))) ->set('selectButtons', $this->getSelectButtons($environment)) ->set('sortable', $this->isSortable($environment)) ->set('showColumns', $showColumn) diff --git a/src/Contao/View/Contao2BackendView/ActionHandler/ShowHandler.php b/src/Contao/View/Contao2BackendView/ActionHandler/ShowHandler.php index 28d1c637..2267b179 100644 --- a/src/Contao/View/Contao2BackendView/ActionHandler/ShowHandler.php +++ b/src/Contao/View/Contao2BackendView/ActionHandler/ShowHandler.php @@ -41,6 +41,7 @@ use ContaoCommunityAlliance\Translator\TranslatorInterface; use Contao\StringUtil; use LogicException; + use function array_merge; use function array_values; use function sprintf; diff --git a/src/Contao/View/Contao2BackendView/FileSelect.php b/src/Contao/View/Contao2BackendView/FileSelect.php index 4406a30f..72d677b3 100644 --- a/src/Contao/View/Contao2BackendView/FileSelect.php +++ b/src/Contao/View/Contao2BackendView/FileSelect.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,8 @@ * @package contao-community-alliance/dc-general * @author Sven Baumann * @author Richard Henkenjohann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -129,7 +130,7 @@ public function run() $template->charset = $GLOBALS['TL_CONFIG']['characterSet']; $template->addSearch = $fileSelector->searchField; $template->search = $GLOBALS['TL_LANG']['MSC']['search']; - $template->action = \ampersand(Environment::get('request')); + $template->action = StringUtil::ampersand(Environment::get('request')); $template->value = $sessionStorage->get('file_selector_search'); $template->manager = $GLOBALS['TL_LANG']['MSC']['treepickerManager']; $template->managerHref = ''; @@ -152,7 +153,7 @@ public function run() if (Input::get('switch') && $user->hasAccess('page', 'modules')) { $template->switch = $GLOBALS['TL_LANG']['MSC']['pagePicker']; $template->switchHref = - \str_replace('contao/file.php', 'contao/page.php', \ampersand(Environment::get('request'))); + \str_replace('contao/file.php', 'contao/page.php', StringUtil::ampersand(Environment::get('request'))); } // Prevent debug output at all cost. diff --git a/src/Contao/View/Contao2BackendView/PanelRenderer.php b/src/Contao/View/Contao2BackendView/PanelRenderer.php index a8183dfb..31ecae19 100644 --- a/src/Contao/View/Contao2BackendView/PanelRenderer.php +++ b/src/Contao/View/Contao2BackendView/PanelRenderer.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2021 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -15,13 +15,15 @@ * @author Stefan Heimes * @author binron * @author Sven Baumann - * @copyright 2013-2021 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ namespace ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView; +use Contao\StringUtil; use ContaoCommunityAlliance\Contao\Bindings\ContaoEvents; use ContaoCommunityAlliance\Contao\Bindings\Events\Backend\GetThemeEvent; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\GetPanelElementTemplateEvent; @@ -193,7 +195,7 @@ public function render($ignoredPanels = []) $environment->getEventDispatcher()->dispatch($themeEvent, ContaoEvents::BACKEND_GET_THEME); $template - ->set('action', \ampersand($environment->getInputProvider()->getRequestUrl(), true)) + ->set('action', StringUtil::ampersand($environment->getInputProvider()->getRequestUrl(), true)) ->set('theme', $themeEvent->getTheme()) ->set('panel', $panels); diff --git a/src/Contao/View/Contao2BackendView/Subscriber/GetGroupHeaderSubscriber.php b/src/Contao/View/Contao2BackendView/Subscriber/GetGroupHeaderSubscriber.php index 89c3b617..7426536d 100644 --- a/src/Contao/View/Contao2BackendView/Subscriber/GetGroupHeaderSubscriber.php +++ b/src/Contao/View/Contao2BackendView/Subscriber/GetGroupHeaderSubscriber.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2022 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -16,13 +16,14 @@ * @author Stefan Heimes * @author Sven Baumann * @author Ingolf Steinhardt - * @copyright 2013-2022 Contao Community Alliance. + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ namespace ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Subscriber; +use Contao\ArrayUtil; use Contao\Config; use ContaoCommunityAlliance\Contao\Bindings\ContaoEvents; use ContaoCommunityAlliance\Contao\Bindings\Events\Date\ParseDateEvent; @@ -138,7 +139,7 @@ protected function formatGroupHeader( if (isset($evaluation['reference'])) { $remoteNew = $evaluation['reference'][$value]; - } elseif (\array_is_assoc($property->getOptions())) { + } elseif (ArrayUtil::isAssoc($property->getOptions())) { $options = $property->getOptions(); $remoteNew = $options[$value]; } else { diff --git a/src/Contao/View/Contao2BackendView/Subscriber/WidgetBuilder.php b/src/Contao/View/Contao2BackendView/Subscriber/WidgetBuilder.php index e63e6bce..16be0f3e 100644 --- a/src/Contao/View/Contao2BackendView/Subscriber/WidgetBuilder.php +++ b/src/Contao/View/Contao2BackendView/Subscriber/WidgetBuilder.php @@ -259,7 +259,7 @@ protected function getTableWizard() return \sprintf( ' %s %s%s', - \ampersand($urlEvent->getUrl()), + StringUtil::ampersand($urlEvent->getUrl()), StringUtil::specialchars($translator->translate('importTable.1', $defName)), $importTableEvent->getHtml(), $shrinkEvent->getHtml(), @@ -292,7 +292,7 @@ protected function getListWizard() return \sprintf( ' %s', - \ampersand($urlEvent->getUrl()), + StringUtil::ampersand($urlEvent->getUrl()), StringUtil::specialchars($translator->translate('importList.1', $defName)), $importListEvent->getHtml() ); diff --git a/src/Contao/View/Contao2BackendView/TreeSelect.php b/src/Contao/View/Contao2BackendView/TreeSelect.php index f9454364..f67c0f88 100644 --- a/src/Contao/View/Contao2BackendView/TreeSelect.php +++ b/src/Contao/View/Contao2BackendView/TreeSelect.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -15,7 +15,8 @@ * @author Stefan Heimes * @author Sven Baumann * @author Richard Henkenjohann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -169,7 +170,7 @@ public function run() ->set('charset', $GLOBALS['TL_CONFIG']['characterSet']) ->set('addSearch', $treeSelector->searchField) ->set('search', $GLOBALS['TL_LANG']['MSC']['search']) - ->set('action', \ampersand(Environment::get('request'))) + ->set('action', StringUtil::ampersand(Environment::get('request'))) ->set('value', $sessionStorage->get($treeSelector->getSearchSessionKey())) ->set('manager', $GLOBALS['TL_LANG']['MSC']['treepickerManager']) ->set('breadcrumb', $GLOBALS['TL_DCA'][$treeSelector->foreignTable]['list']['sorting']['breadcrumb']) @@ -178,7 +179,7 @@ public function run() // Add the manager link. if ($treeSelector->managerHref) { $template - ->set('managerHref', 'contao?' . \ampersand($treeSelector->managerHref) . '&popup=1'); + ->set('managerHref', 'contao?' . StringUtil::ampersand($treeSelector->managerHref) . '&popup=1'); } // Prevent debug output at all cost. diff --git a/src/Controller/BackendTreeController.php b/src/Controller/BackendTreeController.php index d9f3ff88..a1555437 100644 --- a/src/Controller/BackendTreeController.php +++ b/src/Controller/BackendTreeController.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Sven Baumann * @author Kim Wormer - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -151,7 +152,7 @@ private function runBackendTree(Request $request) ->set('charset', $GLOBALS['TL_CONFIG']['characterSet']) ->set('addSearch', $treeSelector->searchField) ->set('search', $GLOBALS['TL_LANG']['MSC']['search']) - ->set('action', \ampersand($request->getUri())) + ->set('action', StringUtil::ampersand($request->getUri())) ->set('value', $sessionBag->get($treeSelector->getSearchSessionKey())) ->set('manager', $GLOBALS['TL_LANG']['MSC']['treepickerManager'] ?? '') ->set('breadcrumb', $GLOBALS['TL_DCA'][$treeSelector->foreignTable]['list']['sorting']['breadcrumb'] ?? ''); @@ -197,7 +198,7 @@ private function runBackendTreeBreadCrumb(Request $request) ->set('title', StringUtil::specialchars($GLOBALS['TL_LANG']['MSC']['treepicker'])) ->set('charset', $GLOBALS['TL_CONFIG']['characterSet']) ->set('search', $GLOBALS['TL_LANG']['MSC']['search']) - ->set('action', \ampersand($request->getUri())) + ->set('action', StringUtil::ampersand($request->getUri())) ->set('manager', $GLOBALS['TL_LANG']['MSC']['treepickerManager']); return $template->getResponse(); diff --git a/src/Controller/DefaultController.php b/src/Controller/DefaultController.php index baa5683f..b9ed23a1 100644 --- a/src/Controller/DefaultController.php +++ b/src/Controller/DefaultController.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2022 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -25,7 +25,7 @@ * @author Sven Baumann * @author Ingolf Steinhardt * @author Tim Gatzky - * @copyright 2013-2022 Contao Community Alliance. + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -676,7 +676,8 @@ private function applyAction(array &$action, array &$deepCopyList, ModelInterfac /** @var ModelInterface|null $model */ $model = $action['model']; /** @var ItemInterface $item */ - $item = $action['item']; + $item = $action['item']; + $isDeepCopy = false; if ($item->isCreate()) { // create new model @@ -686,7 +687,7 @@ private function applyAction(array &$action, array &$deepCopyList, ModelInterfac $model = $this->modelCollector->getModel(ModelId::fromModel($model)); $clonedModel = $this->doCloneAction($model); - if (isset($isDeepCopy) && $isDeepCopy) { + if ($isDeepCopy) { $deepCopyList[] = [ 'origin' => $model, 'model' => $clonedModel, diff --git a/src/Data/DefaultCollection.php b/src/Data/DefaultCollection.php index 79fc13ad..c9138549 100644 --- a/src/Data/DefaultCollection.php +++ b/src/Data/DefaultCollection.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -18,13 +18,15 @@ * @author Patrick Kahl * @author David Molineus * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ namespace ContaoCommunityAlliance\DcGeneral\Data; +use Contao\ArrayUtil; use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralRuntimeException; /** @@ -44,14 +46,14 @@ class DefaultCollection implements CollectionInterface * * @var ModelInterface[] */ - protected $arrCollection = []; + protected array $arrCollection = []; /** * Get length of this collection. * * @return int */ - public function length() + public function length(): int { return \count($this->arrCollection); } @@ -59,7 +61,7 @@ public function length() /** * {@inheritdoc} */ - public function count() + public function count(): int { return $this->length(); } @@ -67,7 +69,7 @@ public function count() /** * {@inheritdoc} */ - public function offsetExists($offset) + public function offsetExists($offset): bool { return \array_key_exists($offset, $this->arrCollection); } @@ -190,7 +192,7 @@ public function shift() public function insert($index, ModelInterface $model) { if ($model->hasProperties()) { - \array_insert($this->arrCollection, $index, [$model]); + ArrayUtil::arrayInsert($this->arrCollection, $index, [$model]); } } diff --git a/src/Data/DefaultDataProvider.php b/src/Data/DefaultDataProvider.php index fb246352..9d89582a 100644 --- a/src/Data/DefaultDataProvider.php +++ b/src/Data/DefaultDataProvider.php @@ -1,1040 +1,1040 @@ - - * @author Stefan Heimes - * @author Tristan Lins - * @author Andreas Isaak - * @author David Molineus - * @author Oliver Hoff - * @author Patrick Kahl - * @author Simon Kusterer - * @author Christopher Boelter - * @author Sven Baumann - * @author Ingolf Steinhardt - * @author Richard Henkenjohann - * @author Alex Wuttke - * @copyright 2013-2023 Contao Community Alliance. - * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later - * @filesource - */ - -namespace ContaoCommunityAlliance\DcGeneral\Data; - -use Contao\BackendUser; -use Contao\Database; -use Contao\StringUtil; -use Contao\System; -use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralRuntimeException; -use Doctrine\DBAL\Connection; -use Doctrine\DBAL\Schema\Table; - -/** - * Class DefaultDataProvider. - * - * Default implementation for a data provider using the Contao default database as backend. - * - * @SuppressWarnings(PHPMD.TooManyPublicMethods) - We have to keep them as we implement the interfaces. - * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) - There is no elegant way to reduce this class more without - * reducing the interface. - * @SuppressWarnings(PHPMD.CouplingBetweenObjects) - Can not reduce without changing the interface. - */ -class DefaultDataProvider implements DataProviderInterface -{ - /** - * Name of current source. - * - * @var string - */ - protected $source; - - /** - * The Database instance. - * - * @var Connection - */ - protected $connection; - - /** - * The name of the id property. - * - * @var string - */ - protected $idProperty = 'id'; - - /** - * The property that shall get populated with the current timestamp when saving data. - * - * @var string - */ - protected $timeStampProperty = false; - - /** - * The id generator to use (if any). - * - * @var IdGeneratorInterface - */ - protected $idGenerator; - - /** - * The table schema. - * - * @var Table - */ - private $schema; - - /** - * Retrieve the name of the id property. - * - * @return string - */ - public function getIdProperty() - { - return $this->idProperty; - } - - /** - * Set the id property. - * - * @param string $idProperty The name of the id property. - * - * @return DefaultDataProvider - */ - public function setIdProperty($idProperty) - { - $this->idProperty = $idProperty; - - return $this; - } - - /** - * Get the property name that shall get updated with the current time stamp when saving to the database. - * - * @return string|null - */ - public function getTimeStampProperty() - { - return $this->timeStampProperty; - } - - /** - * Set the property name that shall get updated with the current time stamp when saving to the database. - * - * @param boolean $timeStampField The property name or empty to clear. - * - * @return DefaultDataProvider - */ - public function setTimeStampProperty($timeStampField = null) - { - $this->timeStampProperty = $timeStampField; - - return $this; - } - - /** - * Set the id generator to use. - * - * @param IdGeneratorInterface $idGenerator The id generator. - * - * @return DefaultDataProvider - */ - public function setIdGenerator($idGenerator) - { - $this->idGenerator = $idGenerator; - - return $this; - } - - /** - * Retrieve the id generator. - * - * @return IdGeneratorInterface - */ - public function getIdGenerator() - { - return $this->idGenerator; - } - - /** - * Create an instance of the default database driven uuid generator. - * - * @return DefaultDataProvider - * - * @throws DcGeneralRuntimeException When already an id generator has been set on the instance. - */ - public function enableDefaultUuidGenerator() - { - if ($this->idGenerator) { - throw new DcGeneralRuntimeException('Error: already an id generator set on database provider.'); - } - - $this->setIdGenerator(new DatabaseUuidIdGenerator($this->connection)); - - return $this; - } - - /** - * {@inheritDoc} - * - * @throws DcGeneralRuntimeException When no source has been defined. - * @throws DcGeneralRuntimeException For invalid database connection. - * - * @SuppressWarnings(PHPMD.CyclomaticComplexity) - * @SuppressWarnings(PHPMD.NPathComplexity) - */ - public function setBaseConfig(array $config) - { - // Check configuration. - if (!isset($config['source'])) { - throw new DcGeneralRuntimeException('Missing table name.'); - } - - $this->fallbackFromDatabaseToConnection($config); - - if (isset($config['connection'])) { - if (!($config['connection'] instanceof Connection)) { - throw new DcGeneralRuntimeException('Invalid database connection.'); - } - - $this->connection = $config['connection']; - } else { - // @codingStandardsIgnoreStart - @\trigger_error( - 'You should pass a doctrine database connection to "' . __METHOD__ . '".', - E_USER_DEPRECATED - ); - // @codingStandardsIgnoreEnd - - $this->connection = $this->getDefaultConnection(); - } - - if (isset($config['idGenerator'])) { - if ($config['idGenerator'] instanceof IdGeneratorInterface) { - $idGenerator = $config['idGenerator']; - $this->setIdGenerator($idGenerator); - } - } - - $this->source = $config['source']; - - if (isset($config['timeStampProperty'])) { - $this->setTimeStampProperty($config['timeStampProperty']); - } elseif ($this->fieldExists('tstamp')) { - $this->setTimeStampProperty('tstamp'); - } - - if (isset($config['idProperty'])) { - $this->setIdProperty($config['idProperty']); - } - } - - /** - * {@inheritDoc} - */ - public function getEmptyConfig() - { - return DefaultConfig::init(); - } - - /** - * {@inheritDoc} - */ - public function getEmptyModel() - { - $model = new DefaultModel(); - $model->setProviderName($this->source); - return $model; - } - - /** - * {@inheritDoc} - */ - public function getEmptyCollection() - { - return new DefaultCollection(); - } - - /** - * Fetch an empty single filter option collection (new model list). - * - * @return FilterOptionCollectionInterface - * - * @deprecated This method was never intended to be used externally. - */ - public function getEmptyFilterOptionCollection() - { - // @codingStandardsIgnoreStart - @\trigger_error( - 'Method ' . __METHOD__ . ' was never intended to be called via interface and will get removed', - E_USER_DEPRECATED - ); - // @codingStandardsIgnoreEnd - return new DefaultFilterOptionCollection(); - } - - /** - * {@inheritDoc} - * - * @throws DcGeneralRuntimeException When an unusable object has been passed. - */ - public function delete($item) - { - $modelId = null; - if (\is_numeric($item) || \is_string($item)) { - $modelId = $item; - } elseif (\is_object($item) && $item instanceof ModelInterface && null !== $item->getId()) { - $modelId = $item->getId(); - } else { - throw new DcGeneralRuntimeException("ID missing or given object not of type 'ModelInterface'."); - } - - // Insert undo. - $this->insertUndo( - \sprintf( - 'DELETE FROM %1$s WHERE %1$s.id = %2$s', - $this->source, - $modelId - ), - \sprintf( - 'SELECT * FROM %1$s WHERE %1$s.id = %2$s', - $this->source, - $modelId - ), - $this->source - ); - - $this->connection->delete($this->source, [$this->source . '.id' => $modelId]); - } - - /** - * Create a model from a database result. - * - * @param array $result The database result to create a model from. - * - * @return ModelInterface - */ - protected function createModelFromDatabaseResult(array $result) - { - $model = $this->getEmptyModel(); - - foreach ($result as $key => $value) { - if ($key === $this->idProperty) { - $model->setIdRaw($value); - } - - $model->setPropertyRaw($key, StringUtil::deserialize($value)); - } - - return $model; - } - - /** - * {@inheritDoc} - * - * @throws \Doctrine\DBAL\Exception - */ - public function fetch(ConfigInterface $config) - { - $queryBuilder = $this->connection->createQueryBuilder(); - $queryBuilder->from($this->source); - DefaultDataProviderDBalUtils::addField($config, $this->idProperty, $queryBuilder); - - if (null !== $config->getId()) { - $queryBuilder->where($this->source . '.id=:id'); - $queryBuilder->setParameter('id', $config->getId()); - } - - if (null === $config->getId()) { - DefaultDataProviderDBalUtils::addWhere($config, $queryBuilder); - DefaultDataProviderDBalUtils::addSorting($config, $queryBuilder); - - $queryBuilder->setMaxResults(1); - $queryBuilder->setFirstResult(0); - } - - $statement = $queryBuilder->executeQuery(); - if (0 === $statement->rowCount()) { - return null; - } - - $result = $statement->fetchAssociative(); - - return $this->createModelFromDatabaseResult($result); - } - - /** - * {@inheritDoc} - * - * @throws \Doctrine\DBAL\Exception - */ - public function fetchAll(ConfigInterface $config) - { - $queryBuilder = $this->connection->createQueryBuilder(); - $queryBuilder->from($this->source); - DefaultDataProviderDBalUtils::addField($config, $this->idProperty, $queryBuilder); - DefaultDataProviderDBalUtils::addWhere($config, $queryBuilder); - DefaultDataProviderDBalUtils::addSorting($config, $queryBuilder); - - if (0 !== $config->getAmount()) { - $queryBuilder->setMaxResults($config->getAmount()); - $queryBuilder->setFirstResult($config->getStart() ?? 0); - } - - $collection = $this->getEmptyCollection(); - - $statement = $queryBuilder->executeQuery(); - if (0 === $statement->rowCount()) { - return $collection; - } - - if ($config->getIdOnly()) { - return $statement->fetchFirstColumn(); - } - - $result = $statement->fetchAllAssociative(); - - foreach ($result as $item) { - $collection->push($this->createModelFromDatabaseResult($item)); - } - - return $collection; - } - - /** - * {@inheritDoc} - * - * @throws DcGeneralRuntimeException If improper values have been passed (i.e. not exactly one field requested). - * @throws \Doctrine\DBAL\Exception - */ - public function getFilterOptions(ConfigInterface $config) - { - $internalConfig = $this->prefixDataProviderProperties($config); - $properties = $internalConfig->getFields(); - if (1 !== \count($properties)) { - throw new DcGeneralRuntimeException('objConfig must contain exactly one property to be retrieved.'); - } - $property = $properties[0]; - - $queryBuilder = $this->connection->createQueryBuilder(); - $queryBuilder->select('DISTINCT(' . $property . ')'); - $queryBuilder->from($this->source); - DefaultDataProviderDBalUtils::addWhere($internalConfig, $queryBuilder); - $queryBuilder->addOrderBy($property); - - $statement = $queryBuilder->executeQuery(); - - $values = $statement->fetchAllAssociative(); - - $filterProperties = $config->getFields(); - if (1 !== \count($filterProperties)) { - throw new DcGeneralRuntimeException('objConfig must contain exactly one property to be retrieved.'); - } - $filterProperty = $filterProperties[0]; - - $collection = new DefaultFilterOptionCollection(); - foreach ($values as $value) { - $collection->add($value[$filterProperty], $value[$filterProperty]); - } - - return $collection; - } - - /** - * {@inheritDoc} - * - * @throws \Doctrine\DBAL\Exception - */ - public function getCount(ConfigInterface $config) - { - $internalConfig = $this->prefixDataProviderProperties($config); - $queryBuilder = $this->connection->createQueryBuilder(); - $queryBuilder->select('COUNT(*) AS count'); - $queryBuilder->from($this->source); - DefaultDataProviderDBalUtils::addWhere($internalConfig, $queryBuilder); - - $statement = $queryBuilder->executeQuery(); - - return $statement->fetchFirstColumn(); - } - - /** - * {@inheritDoc} - * @throws \Doctrine\DBAL\Exception - */ - public function isUniqueValue($field, $new, $primaryId = null) - { - $queryBuilder = $this->connection->createQueryBuilder(); - $queryBuilder->select('*'); - $queryBuilder->from($this->source); - $queryBuilder->where($queryBuilder->expr()->eq($this->source . '.' . $field, ':' . $field)); - $queryBuilder->setParameter($field, $new); - - $statement = $queryBuilder->executeQuery(); - $unique = $statement->fetchAssociative(); - - if (0 === $statement->rowCount()) { - return true; - } - - if (($primaryId === $unique['id']) && (1 === $statement->rowCount())) { - return true; - } - - return false; - } - - /** - * {@inheritDoc} - */ - public function resetFallback($field) - { - // @codingStandardsIgnoreStart - @\trigger_error( - __CLASS__ . '::' . __METHOD__ . ' is deprecated - handle resetting manually', - E_USER_DEPRECATED - ); - // @codingStandardsIgnoreEnd - - $this->connection->query('UPDATE ' . $this->source . ' SET ' . $field . ' = \'\'')->execute(); - } - - /** - * Prefix the data provider properties. - * - * @param ConfigInterface $config The config. - * - * @return ConfigInterface - */ - private function prefixDataProviderProperties(ConfigInterface $config) - { - $internalConfig = clone $config; - $this->sortingPrefixer($internalConfig); - - if (null !== ($filter = $internalConfig->getFilter())) { - $this->filterPrefixer($filter); - $internalConfig->setFilter($filter); - } - - if (null !== ($fields = $internalConfig->getFields())) { - $this->fieldPrefixer($fields); - $internalConfig->setFields($fields); - } - - return $internalConfig; - } - - /** - * The config sorting prefixer. - * - * @param ConfigInterface $config The config. - * - * @return void - */ - private function sortingPrefixer(ConfigInterface $config) - { - $sorting = []; - foreach ($config->getSorting() as $property => $value) { - if (0 === \strpos($property, $this->source . '.')) { - $sorting[$property] = $value; - - continue; - } - - if (!$this->fieldExists($property)) { - continue; - } - - $sorting[$this->source . '.' . $property] = $value; - } - $config->setSorting($sorting); - } - - /** - * The filter prefixer. - * - * @param array $filter The filter setting. - * - * @return void - */ - private function filterPrefixer(array &$filter) - { - foreach ($filter as &$child) { - if ( - \array_key_exists('property', $child) - && (false === \strpos($child['property'], $this->source . '.')) - && $this->fieldExists($child['property']) - ) { - $child['property'] = $this->source . '.' . $child['property']; - } - - if (\array_key_exists('children', $child)) { - $this->filterPrefixer($child['children']); - } - } - } - - /** - * The field prefixer. - * - * @param array $fields The fields. - * - * @return void - */ - private function fieldPrefixer(array &$fields) - { - foreach ($fields as $index => $property) { - if (0 === \strpos($property, $this->source . '.') || !$this->fieldExists($property)) { - continue; - } - - $fields[$index] = $this->source . '.' . $property; - } - } - - /** - * Convert a model into a property array to be used in insert and update queries. - * - * @param ModelInterface $model The model to convert into an property array. - * @param int $timestamp Optional the timestamp. - * - * @return array - * - * @SuppressWarnings(PHPMD.Superglobals) - */ - private function convertModelToDataPropertyArray(ModelInterface $model, int $timestamp) - { - $data = []; - foreach ($model as $key => $value) { - if (($key === $this->idProperty) || !$this->fieldExists($key)) { - continue; - } - - if (\is_array($value)) { - $data[$this->source . '.' . $key] = \serialize($value); - } else { - $data[$this->source . '.' . $key] = $value; - } - } - - if ($this->timeStampProperty) { - $data[$this->source . '.' . $this->getTimeStampProperty()] = $timestamp ?: \time(); - } - - return $data; - } - - /** - * Insert the model into the database. - * - * @param ModelInterface $model The model to insert into the database. - * @param int $timestamp Optional the timestamp. - * - * @return void - */ - private function insertModelIntoDatabase(ModelInterface $model, $timestamp = 0): void - { - $data = $this->convertModelToDataPropertyArray($model, $timestamp); - if ($this->getIdGenerator()) { - $model->setId($this->getIdGenerator()->generate()); - $data[$this->idProperty] = $model->getId(); - } - - $this->connection->insert($this->source, $data); - - $insertId = $this->connection->lastInsertId($this->source); - - if (('' !== $insertId) && !isset($data[$this->idProperty])) { - // Retrieve id with query to set type. - $modelId = $this->connection->createQueryBuilder() - ->select('t.id') - ->from($this->source, 't') - ->where('t.id=:id') - ->setParameter('id', $insertId) - ->executeQuery() - ->fetchOne(); - - $model->setId(($modelId ?? $insertId)); - } - } - - /** - * Update the model in the database. - * - * @param ModelInterface $model The model to update the database. - * @param int $timestamp Optional the timestamp. - * - * @return void - */ - private function updateModelInDatabase($model, $timestamp = 0) - { - $data = $this->convertModelToDataPropertyArray($model, $timestamp); - - $this->connection->update($this->source, $data, ['id' => $model->getId()]); - } - - /** - * {@inheritDoc} - */ - public function save(ModelInterface $item, $timestamp = 0) - { - if (\in_array($item->getId(), [null, ''])) { - $this->insertModelIntoDatabase($item); - } else { - $this->updateModelInDatabase($item); - } - - return $item; - } - - /** - * {@inheritDoc} - */ - public function saveEach(CollectionInterface $items, $timestamp = 0) - { - foreach ($items as $value) { - $this->save($value, $timestamp); - } - } - - /** - * {@inheritDoc} - */ - public function fieldExists($columnName) - { - if (!isset($this->schema)) { - $this->schema = $this->connection->getSchemaManager()->listTableDetails($this->source); - } - - return $this->schema->hasColumn($columnName); - } - - /** - * {@inheritDoc} - * - * @throws \Doctrine\DBAL\Exception - */ - public function getVersion($mixID, $mixVersion) - { - $queryBuilder = $this->connection->createQueryBuilder(); - $queryBuilder->select('*'); - $queryBuilder->from('tl_version'); - $queryBuilder->andWhere($queryBuilder->expr()->eq('tl_version.pid', ':pid')); - $queryBuilder->setParameter('pid', $mixID); - $queryBuilder->andWhere($queryBuilder->expr()->eq('tl_version.version', ':version')); - $queryBuilder->setParameter('version', $mixVersion); - $queryBuilder->andWhere($queryBuilder->expr()->eq('tl_version.fromTable', ':fromTable')); - $queryBuilder->setParameter('fromTable', $this->source); - - $statement = $queryBuilder->executeQuery(); - if (0 === $statement->rowCount()) { - return null; - } - - if (null === $version = $statement->fetchAllAssociative()) { - return null; - } - - $data = StringUtil::deserialize($version['data']); - - if (!\is_array($data) || (0 === \count($data))) { - return null; - } - - $model = $this->getEmptyModel(); - $model->setID($mixID); - foreach ($data as $key => $value) { - if ($key === $this->idProperty) { - continue; - } - - $model->setProperty($key, $value); - } - - return $model; - } - - /** - * Return a list with all versions for the row with the given Id. - * - * @param mixed $mixID The ID of the row. - * @param boolean $onlyActive If true, only active versions will get returned, if false all version will get - * returned. - * - * @return CollectionInterface - * - * @throws \Doctrine\DBAL\Exception - */ - public function getVersions($mixID, $onlyActive = false) - { - $queryBuilder = $this->connection->createQueryBuilder(); - $queryBuilder->select(['tstamp', 'version', 'username', 'active']); - $queryBuilder->from('tl_version'); - $queryBuilder->andWhere($queryBuilder->expr()->eq('tl_version.fromTable', ':fromTable')); - $queryBuilder->setParameter('fromTable', $this->source); - $queryBuilder->andWhere($queryBuilder->expr()->eq('tl_version.pid', ':pid')); - $queryBuilder->setParameter('pid', $mixID); - - if ($onlyActive) { - $queryBuilder->andWhere($queryBuilder->expr()->eq('tl_version.active', ':active')); - $queryBuilder->setParameter('active', '1'); - } else { - $queryBuilder->orderBy('tl_version.version', 'DESC'); - } - - $statement = $queryBuilder->executeQuery(); - if (0 === $statement->rowCount()) { - return null; - } - - $versions = $statement->fetchAllAssociative(); - - $collection = $this->getEmptyCollection(); - - foreach ((array) $versions as $versionValue) { - $model = $this->getEmptyModel(); - $model->setId($mixID); - - foreach ($versionValue as $key => $value) { - if ($key === $this->idProperty) { - continue; - } - - $model->setProperty($key, $value); - } - - $collection->push($model); - } - - return $collection; - } - - /** - * Save a new version of a row. - * - * @param ModelInterface $model The model for which a new version shall be created. - * @param string $username The username to attach to the version as creator. - * - * @return void - * - * @throws \Doctrine\DBAL\Exception - */ - public function saveVersion(ModelInterface $model, $username) - { - $queryBuilder = $this->connection->createQueryBuilder(); - $queryBuilder->select('COUNT(*) AS count'); - $queryBuilder->from('tl_version'); - $queryBuilder->andWhere($queryBuilder->expr()->eq('tl_version.pid', ':pid')); - $queryBuilder->setParameter('pid', $model->getId()); - $queryBuilder->andWhere($queryBuilder->expr()->eq('tl_version.fromTable', ':fromTable')); - $queryBuilder->setParameter('fromTable', $this->source); - - $statement = $queryBuilder->executeQuery(); - $count = $statement->fetchFirstColumn(); - - $mixNewVersion = ((int) $count + 1); - $mixData = $model->getPropertiesAsArray(); - - $mixData[$this->idProperty] = $model->getId(); - - $insert = [ - 'tl_version.pid' => $model->getId(), - 'tl_version.tstamp' => \time(), - 'tl_version.version' => $mixNewVersion, - 'tl_version.fromTable' => $this->source, - 'tl_version.username' => $username, - 'tl_version.data' => \serialize($mixData) - ]; - - $this->connection->insert('tl_version', $insert); - - $this->setVersionActive($model->getId(), $mixNewVersion); - } - - /** - * Set a version as active. - * - * @param mixed $mixID The ID of the row. - * @param mixed $mixVersion The version number to set active. - * - * @return void - */ - public function setVersionActive($mixID, $mixVersion) - { - $updateValues = ['tl_version.pid' => $mixID, 'tl_version.fromTable' => $this->source]; - - // Set version inactive. - $this->connection->update('tl_version', ['tl_version.active' => ''], $updateValues); - - // Set version active. - $updateValues['version'] = $mixVersion; - $this->connection->update('tl_version', ['tl_version.active' => 1], $updateValues); - } - - /** - * Retrieve the current active version for a row. - * - * @param mixed $mixID The ID of the row. - * - * @return mixed The current version number of the requested row. - * - * @throws \Doctrine\DBAL\Exception - */ - public function getActiveVersion($mixID) - { - $queryBuilder = $this->connection->createQueryBuilder(); - $queryBuilder->select('version'); - $queryBuilder->from('tl_version'); - $queryBuilder->andWhere($queryBuilder->expr()->eq('tl_version.pid', ':pid')); - $queryBuilder->setParameter('pid', $mixID); - $queryBuilder->andWhere($queryBuilder->expr()->eq('tl_version.fromTable', ':fromTable')); - $queryBuilder->setParameter('fromTable', $this->source); - $queryBuilder->andWhere($queryBuilder->expr()->eq('tl_version.active', ':active')); - $queryBuilder->setParameter('active', 1); - - $statement = $queryBuilder->executeQuery(); - if (0 === $statement->rowCount()) { - return null; - } - - return $statement->fetchAllAssociative()['version']; - } - - /** - * Check if two models have the same values in all properties. - * - * @param ModelInterface $firstModel The first model to compare. - * @param ModelInterface $secondModel The second model to compare. - * - * @return boolean True - If both models are same, false if not. - */ - public function sameModels($firstModel, $secondModel) - { - foreach ($firstModel as $key => $value) { - if ($key === $this->idProperty) { - continue; - } - - if (\is_array($value)) { - if (!\is_array($secondModel->getProperty($key))) { - return false; - } - - if (\serialize($value) !== \serialize($secondModel->getProperty($key))) { - return false; - } - } elseif ($value !== $secondModel->getProperty($key)) { - return false; - } - } - - return true; - } - - /** - * Store an undo entry in the table tl_undo. - * - * Currently, this only supports delete queries. - * - * @param string $sourceSQL The SQL used to perform the action to be undone. - * @param string $saveSQL The SQL query to retrieve the current entries. - * @param string $table The table to be affected by the action. - * - * @return void - * @throws \Doctrine\DBAL\Exception - */ - protected function insertUndo($sourceSQL, $saveSQL, $table) - { - // Load row. - $statement = $this->connection->executeQuery($saveSQL); - - // Check if we have a result. - if (0 === $statement->rowCount()) { - return; - } - - $result = $statement->fetchAssociative(); - - // Save information in array. - $parameters = []; - foreach ($result as $value) { - $parameters[$table][] = $value; - } - - $prefix = '(DC General)'; - $user = BackendUser::getInstance(); - - // Write into undo. - $this->connection->insert( - 'tl_undo', - [ - 'tl_undo.pid' => $user->id, - 'tl_undo.tstamp' => \time(), - 'tl_undo.fromTable' => $table, - 'tl_undo.query' => $prefix . $sourceSQL, - 'tl_undo.affectedRows' => \count($parameters[$table]), - 'tl_undo.data' => \serialize($parameters) - ] - ); - } - - /** - * Get the default connection for the database. - * - * @return Connection - */ - protected function getDefaultConnection() - { - return System::getContainer()->get('database_connection'); - } - - /** - * This is a fallback for get the connection instead of the old database. - * - * @param array $config The configuration. - * - * @return void - * - * @deprecated This method is deprecated since 2.1 and where removed in 3.0. The old database never used in future. - */ - private function fallbackFromDatabaseToConnection(array &$config) - { - if (isset($config['database'])) { - // @codingStandardsIgnoreStart - @\trigger_error( - 'Config key database is deprecated use instead connection. Fallback will be dropped.', - E_USER_DEPRECATED - ); - // @codingStandardsIgnoreEnd - - if (!isset($config['connection'])) { - $config['connection'] = $config['database']; - } - - unset($config['database']); - } - - if (isset($config['connection']) && $config['connection'] instanceof Database) { - // @codingStandardsIgnoreStart - @\trigger_error( - '"' . __METHOD__ . '" now accepts doctrine instances - ' . - 'passing Contao database instances is deprecated.', - E_USER_DEPRECATED - ); - // @codingStandardsIgnoreEnd - $reflection = new \ReflectionProperty(Database::class, 'resConnection'); - $reflection->setAccessible(true); - - $config['connection'] = $reflection->getValue($config['connection']); - } - } -} + + * @author Stefan Heimes + * @author Tristan Lins + * @author Andreas Isaak + * @author David Molineus + * @author Oliver Hoff + * @author Patrick Kahl + * @author Simon Kusterer + * @author Christopher Boelter + * @author Sven Baumann + * @author Ingolf Steinhardt + * @author Richard Henkenjohann + * @author Alex Wuttke + * @copyright 2013-2023 Contao Community Alliance. + * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later + * @filesource + */ + +namespace ContaoCommunityAlliance\DcGeneral\Data; + +use Contao\BackendUser; +use Contao\Database; +use Contao\StringUtil; +use Contao\System; +use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralRuntimeException; +use Doctrine\DBAL\Connection; +use Doctrine\DBAL\Schema\Table; + +/** + * Class DefaultDataProvider. + * + * Default implementation for a data provider using the Contao default database as backend. + * + * @SuppressWarnings(PHPMD.TooManyPublicMethods) - We have to keep them as we implement the interfaces. + * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) - There is no elegant way to reduce this class more without + * reducing the interface. + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) - Can not reduce without changing the interface. + */ +class DefaultDataProvider implements DataProviderInterface +{ + /** + * Name of current source. + * + * @var string + */ + protected $source; + + /** + * The Database instance. + * + * @var Connection + */ + protected $connection; + + /** + * The name of the id property. + * + * @var string + */ + protected $idProperty = 'id'; + + /** + * The property that shall get populated with the current timestamp when saving data. + * + * @var string + */ + protected $timeStampProperty = false; + + /** + * The id generator to use (if any). + * + * @var IdGeneratorInterface + */ + protected $idGenerator; + + /** + * The table schema. + * + * @var Table + */ + private $schema; + + /** + * Retrieve the name of the id property. + * + * @return string + */ + public function getIdProperty() + { + return $this->idProperty; + } + + /** + * Set the id property. + * + * @param string $idProperty The name of the id property. + * + * @return DefaultDataProvider + */ + public function setIdProperty($idProperty) + { + $this->idProperty = $idProperty; + + return $this; + } + + /** + * Get the property name that shall get updated with the current time stamp when saving to the database. + * + * @return string|null + */ + public function getTimeStampProperty() + { + return $this->timeStampProperty; + } + + /** + * Set the property name that shall get updated with the current time stamp when saving to the database. + * + * @param boolean $timeStampField The property name or empty to clear. + * + * @return DefaultDataProvider + */ + public function setTimeStampProperty($timeStampField = null) + { + $this->timeStampProperty = $timeStampField; + + return $this; + } + + /** + * Set the id generator to use. + * + * @param IdGeneratorInterface $idGenerator The id generator. + * + * @return DefaultDataProvider + */ + public function setIdGenerator($idGenerator) + { + $this->idGenerator = $idGenerator; + + return $this; + } + + /** + * Retrieve the id generator. + * + * @return IdGeneratorInterface + */ + public function getIdGenerator() + { + return $this->idGenerator; + } + + /** + * Create an instance of the default database driven uuid generator. + * + * @return DefaultDataProvider + * + * @throws DcGeneralRuntimeException When already an id generator has been set on the instance. + */ + public function enableDefaultUuidGenerator() + { + if ($this->idGenerator) { + throw new DcGeneralRuntimeException('Error: already an id generator set on database provider.'); + } + + $this->setIdGenerator(new DatabaseUuidIdGenerator($this->connection)); + + return $this; + } + + /** + * {@inheritDoc} + * + * @throws DcGeneralRuntimeException When no source has been defined. + * @throws DcGeneralRuntimeException For invalid database connection. + * + * @SuppressWarnings(PHPMD.CyclomaticComplexity) + * @SuppressWarnings(PHPMD.NPathComplexity) + */ + public function setBaseConfig(array $config) + { + // Check configuration. + if (!isset($config['source'])) { + throw new DcGeneralRuntimeException('Missing table name.'); + } + + $this->fallbackFromDatabaseToConnection($config); + + if (isset($config['connection'])) { + if (!($config['connection'] instanceof Connection)) { + throw new DcGeneralRuntimeException('Invalid database connection.'); + } + + $this->connection = $config['connection']; + } else { + // @codingStandardsIgnoreStart + @\trigger_error( + 'You should pass a doctrine database connection to "' . __METHOD__ . '".', + E_USER_DEPRECATED + ); + // @codingStandardsIgnoreEnd + + $this->connection = $this->getDefaultConnection(); + } + + if (isset($config['idGenerator'])) { + if ($config['idGenerator'] instanceof IdGeneratorInterface) { + $idGenerator = $config['idGenerator']; + $this->setIdGenerator($idGenerator); + } + } + + $this->source = $config['source']; + + if (isset($config['timeStampProperty'])) { + $this->setTimeStampProperty($config['timeStampProperty']); + } elseif ($this->fieldExists('tstamp')) { + $this->setTimeStampProperty('tstamp'); + } + + if (isset($config['idProperty'])) { + $this->setIdProperty($config['idProperty']); + } + } + + /** + * {@inheritDoc} + */ + public function getEmptyConfig() + { + return DefaultConfig::init(); + } + + /** + * {@inheritDoc} + */ + public function getEmptyModel() + { + $model = new DefaultModel(); + $model->setProviderName($this->source); + return $model; + } + + /** + * {@inheritDoc} + */ + public function getEmptyCollection() + { + return new DefaultCollection(); + } + + /** + * Fetch an empty single filter option collection (new model list). + * + * @return FilterOptionCollectionInterface + * + * @deprecated This method was never intended to be used externally. + */ + public function getEmptyFilterOptionCollection() + { + // @codingStandardsIgnoreStart + @\trigger_error( + 'Method ' . __METHOD__ . ' was never intended to be called via interface and will get removed', + E_USER_DEPRECATED + ); + // @codingStandardsIgnoreEnd + return new DefaultFilterOptionCollection(); + } + + /** + * {@inheritDoc} + * + * @throws DcGeneralRuntimeException When an unusable object has been passed. + */ + public function delete($item) + { + $modelId = null; + if (\is_numeric($item) || \is_string($item)) { + $modelId = $item; + } elseif (\is_object($item) && $item instanceof ModelInterface && null !== $item->getId()) { + $modelId = $item->getId(); + } else { + throw new DcGeneralRuntimeException("ID missing or given object not of type 'ModelInterface'."); + } + + // Insert undo. + $this->insertUndo( + \sprintf( + 'DELETE FROM %1$s WHERE %1$s.id = %2$s', + $this->source, + $modelId + ), + \sprintf( + 'SELECT * FROM %1$s WHERE %1$s.id = %2$s', + $this->source, + $modelId + ), + $this->source + ); + + $this->connection->delete($this->source, [$this->source . '.id' => $modelId]); + } + + /** + * Create a model from a database result. + * + * @param array $result The database result to create a model from. + * + * @return ModelInterface + */ + protected function createModelFromDatabaseResult(array $result) + { + $model = $this->getEmptyModel(); + + foreach ($result as $key => $value) { + if ($key === $this->idProperty) { + $model->setIdRaw($value); + } + + $model->setPropertyRaw($key, StringUtil::deserialize($value)); + } + + return $model; + } + + /** + * {@inheritDoc} + * + * @throws \Doctrine\DBAL\Exception + */ + public function fetch(ConfigInterface $config) + { + $queryBuilder = $this->connection->createQueryBuilder(); + $queryBuilder->from($this->source); + DefaultDataProviderDBalUtils::addField($config, $this->idProperty, $queryBuilder); + + if (null !== $config->getId()) { + $queryBuilder->where($this->source . '.id=:id'); + $queryBuilder->setParameter('id', $config->getId()); + } + + if (null === $config->getId()) { + DefaultDataProviderDBalUtils::addWhere($config, $queryBuilder); + DefaultDataProviderDBalUtils::addSorting($config, $queryBuilder); + + $queryBuilder->setMaxResults(1); + $queryBuilder->setFirstResult(0); + } + + $statement = $queryBuilder->executeQuery(); + if (0 === $statement->rowCount()) { + return null; + } + + $result = $statement->fetchAssociative(); + + return $this->createModelFromDatabaseResult($result); + } + + /** + * {@inheritDoc} + * + * @throws \Doctrine\DBAL\Exception + */ + public function fetchAll(ConfigInterface $config) + { + $queryBuilder = $this->connection->createQueryBuilder(); + $queryBuilder->from($this->source); + DefaultDataProviderDBalUtils::addField($config, $this->idProperty, $queryBuilder); + DefaultDataProviderDBalUtils::addWhere($config, $queryBuilder); + DefaultDataProviderDBalUtils::addSorting($config, $queryBuilder); + + if (0 !== $config->getAmount()) { + $queryBuilder->setMaxResults($config->getAmount()); + $queryBuilder->setFirstResult($config->getStart() ?? 0); + } + + $collection = $this->getEmptyCollection(); + + $statement = $queryBuilder->executeQuery(); + if (0 === $statement->rowCount()) { + return $collection; + } + + if ($config->getIdOnly()) { + return $statement->fetchFirstColumn(); + } + + $result = $statement->fetchAllAssociative(); + + foreach ($result as $item) { + $collection->push($this->createModelFromDatabaseResult($item)); + } + + return $collection; + } + + /** + * {@inheritDoc} + * + * @throws DcGeneralRuntimeException If improper values have been passed (i.e. not exactly one field requested). + * @throws \Doctrine\DBAL\Exception + */ + public function getFilterOptions(ConfigInterface $config) + { + $internalConfig = $this->prefixDataProviderProperties($config); + $properties = $internalConfig->getFields(); + if (1 !== \count($properties)) { + throw new DcGeneralRuntimeException('objConfig must contain exactly one property to be retrieved.'); + } + $property = $properties[0]; + + $queryBuilder = $this->connection->createQueryBuilder(); + $queryBuilder->select('DISTINCT(' . $property . ')'); + $queryBuilder->from($this->source); + DefaultDataProviderDBalUtils::addWhere($internalConfig, $queryBuilder); + $queryBuilder->addOrderBy($property); + + $statement = $queryBuilder->executeQuery(); + + $values = $statement->fetchAllAssociative(); + + $filterProperties = $config->getFields(); + if (1 !== \count($filterProperties)) { + throw new DcGeneralRuntimeException('objConfig must contain exactly one property to be retrieved.'); + } + $filterProperty = $filterProperties[0]; + + $collection = new DefaultFilterOptionCollection(); + foreach ($values as $value) { + $collection->add($value[$filterProperty], $value[$filterProperty]); + } + + return $collection; + } + + /** + * {@inheritDoc} + * + * @throws \Doctrine\DBAL\Exception + */ + public function getCount(ConfigInterface $config) + { + $internalConfig = $this->prefixDataProviderProperties($config); + $queryBuilder = $this->connection->createQueryBuilder(); + $queryBuilder->select('COUNT(*) AS count'); + $queryBuilder->from($this->source); + DefaultDataProviderDBalUtils::addWhere($internalConfig, $queryBuilder); + + $statement = $queryBuilder->executeQuery(); + + return $statement->fetchFirstColumn(); + } + + /** + * {@inheritDoc} + * @throws \Doctrine\DBAL\Exception + */ + public function isUniqueValue($field, $new, $primaryId = null) + { + $queryBuilder = $this->connection->createQueryBuilder(); + $queryBuilder->select('*'); + $queryBuilder->from($this->source); + $queryBuilder->where($queryBuilder->expr()->eq($this->source . '.' . $field, ':' . $field)); + $queryBuilder->setParameter($field, $new); + + $statement = $queryBuilder->executeQuery(); + $unique = $statement->fetchAssociative(); + + if (0 === $statement->rowCount()) { + return true; + } + + if (($primaryId === $unique['id']) && (1 === $statement->rowCount())) { + return true; + } + + return false; + } + + /** + * {@inheritDoc} + */ + public function resetFallback($field) + { + // @codingStandardsIgnoreStart + @\trigger_error( + __CLASS__ . '::' . __METHOD__ . ' is deprecated - handle resetting manually', + E_USER_DEPRECATED + ); + // @codingStandardsIgnoreEnd + + $this->connection->query('UPDATE ' . $this->source . ' SET ' . $field . ' = \'\'')->execute(); + } + + /** + * Prefix the data provider properties. + * + * @param ConfigInterface $config The config. + * + * @return ConfigInterface + */ + private function prefixDataProviderProperties(ConfigInterface $config) + { + $internalConfig = clone $config; + $this->sortingPrefixer($internalConfig); + + if (null !== ($filter = $internalConfig->getFilter())) { + $this->filterPrefixer($filter); + $internalConfig->setFilter($filter); + } + + if (null !== ($fields = $internalConfig->getFields())) { + $this->fieldPrefixer($fields); + $internalConfig->setFields($fields); + } + + return $internalConfig; + } + + /** + * The config sorting prefixer. + * + * @param ConfigInterface $config The config. + * + * @return void + */ + private function sortingPrefixer(ConfigInterface $config) + { + $sorting = []; + foreach ($config->getSorting() as $property => $value) { + if (0 === \strpos($property, $this->source . '.')) { + $sorting[$property] = $value; + + continue; + } + + if (!$this->fieldExists($property)) { + continue; + } + + $sorting[$this->source . '.' . $property] = $value; + } + $config->setSorting($sorting); + } + + /** + * The filter prefixer. + * + * @param array $filter The filter setting. + * + * @return void + */ + private function filterPrefixer(array &$filter) + { + foreach ($filter as &$child) { + if ( + \array_key_exists('property', $child) + && (false === \strpos($child['property'], $this->source . '.')) + && $this->fieldExists($child['property']) + ) { + $child['property'] = $this->source . '.' . $child['property']; + } + + if (\array_key_exists('children', $child)) { + $this->filterPrefixer($child['children']); + } + } + } + + /** + * The field prefixer. + * + * @param array $fields The fields. + * + * @return void + */ + private function fieldPrefixer(array &$fields) + { + foreach ($fields as $index => $property) { + if (0 === \strpos($property, $this->source . '.') || !$this->fieldExists($property)) { + continue; + } + + $fields[$index] = $this->source . '.' . $property; + } + } + + /** + * Convert a model into a property array to be used in insert and update queries. + * + * @param ModelInterface $model The model to convert into an property array. + * @param int $timestamp Optional the timestamp. + * + * @return array + * + * @SuppressWarnings(PHPMD.Superglobals) + */ + private function convertModelToDataPropertyArray(ModelInterface $model, int $timestamp) + { + $data = []; + foreach ($model as $key => $value) { + if (($key === $this->idProperty) || !$this->fieldExists($key)) { + continue; + } + + if (\is_array($value)) { + $data[$this->source . '.' . $key] = \serialize($value); + } else { + $data[$this->source . '.' . $key] = $value; + } + } + + if ($this->timeStampProperty) { + $data[$this->source . '.' . $this->getTimeStampProperty()] = $timestamp ?: \time(); + } + + return $data; + } + + /** + * Insert the model into the database. + * + * @param ModelInterface $model The model to insert into the database. + * @param int $timestamp Optional the timestamp. + * + * @return void + */ + private function insertModelIntoDatabase(ModelInterface $model, $timestamp = 0): void + { + $data = $this->convertModelToDataPropertyArray($model, $timestamp); + if ($this->getIdGenerator()) { + $model->setId($this->getIdGenerator()->generate()); + $data[$this->idProperty] = $model->getId(); + } + + $this->connection->insert($this->source, $data); + + $insertId = $this->connection->lastInsertId($this->source); + + if (('' !== $insertId) && !isset($data[$this->idProperty])) { + // Retrieve id with query to set type. + $modelId = $this->connection->createQueryBuilder() + ->select('t.id') + ->from($this->source, 't') + ->where('t.id=:id') + ->setParameter('id', $insertId) + ->executeQuery() + ->fetchOne(); + + $model->setId(($modelId ?? $insertId)); + } + } + + /** + * Update the model in the database. + * + * @param ModelInterface $model The model to update the database. + * @param int $timestamp Optional the timestamp. + * + * @return void + */ + private function updateModelInDatabase($model, $timestamp = 0) + { + $data = $this->convertModelToDataPropertyArray($model, $timestamp); + + $this->connection->update($this->source, $data, ['id' => $model->getId()]); + } + + /** + * {@inheritDoc} + */ + public function save(ModelInterface $item, $timestamp = 0) + { + if (\in_array($item->getId(), [null, ''])) { + $this->insertModelIntoDatabase($item); + } else { + $this->updateModelInDatabase($item); + } + + return $item; + } + + /** + * {@inheritDoc} + */ + public function saveEach(CollectionInterface $items, $timestamp = 0) + { + foreach ($items as $value) { + $this->save($value, $timestamp); + } + } + + /** + * {@inheritDoc} + */ + public function fieldExists($columnName) + { + if (!isset($this->schema)) { + $this->schema = $this->connection->getSchemaManager()->listTableDetails($this->source); + } + + return $this->schema->hasColumn($columnName); + } + + /** + * {@inheritDoc} + * + * @throws \Doctrine\DBAL\Exception + */ + public function getVersion($mixID, $mixVersion) + { + $queryBuilder = $this->connection->createQueryBuilder(); + $queryBuilder->select('*'); + $queryBuilder->from('tl_version'); + $queryBuilder->andWhere($queryBuilder->expr()->eq('tl_version.pid', ':pid')); + $queryBuilder->setParameter('pid', $mixID); + $queryBuilder->andWhere($queryBuilder->expr()->eq('tl_version.version', ':version')); + $queryBuilder->setParameter('version', $mixVersion); + $queryBuilder->andWhere($queryBuilder->expr()->eq('tl_version.fromTable', ':fromTable')); + $queryBuilder->setParameter('fromTable', $this->source); + + $statement = $queryBuilder->executeQuery(); + if (0 === $statement->rowCount()) { + return null; + } + + if (null === $version = $statement->fetchAllAssociative()) { + return null; + } + + $data = StringUtil::deserialize($version['data']); + + if (!\is_array($data) || (0 === \count($data))) { + return null; + } + + $model = $this->getEmptyModel(); + $model->setID($mixID); + foreach ($data as $key => $value) { + if ($key === $this->idProperty) { + continue; + } + + $model->setProperty($key, $value); + } + + return $model; + } + + /** + * Return a list with all versions for the row with the given Id. + * + * @param mixed $mixID The ID of the row. + * @param boolean $onlyActive If true, only active versions will get returned, if false all version will get + * returned. + * + * @return CollectionInterface + * + * @throws \Doctrine\DBAL\Exception + */ + public function getVersions($mixID, $onlyActive = false) + { + $queryBuilder = $this->connection->createQueryBuilder(); + $queryBuilder->select(['tstamp', 'version', 'username', 'active']); + $queryBuilder->from('tl_version'); + $queryBuilder->andWhere($queryBuilder->expr()->eq('tl_version.fromTable', ':fromTable')); + $queryBuilder->setParameter('fromTable', $this->source); + $queryBuilder->andWhere($queryBuilder->expr()->eq('tl_version.pid', ':pid')); + $queryBuilder->setParameter('pid', $mixID); + + if ($onlyActive) { + $queryBuilder->andWhere($queryBuilder->expr()->eq('tl_version.active', ':active')); + $queryBuilder->setParameter('active', '1'); + } else { + $queryBuilder->orderBy('tl_version.version', 'DESC'); + } + + $statement = $queryBuilder->executeQuery(); + if (0 === $statement->rowCount()) { + return null; + } + + $versions = $statement->fetchAllAssociative(); + + $collection = $this->getEmptyCollection(); + + foreach ((array) $versions as $versionValue) { + $model = $this->getEmptyModel(); + $model->setId($mixID); + + foreach ($versionValue as $key => $value) { + if ($key === $this->idProperty) { + continue; + } + + $model->setProperty($key, $value); + } + + $collection->push($model); + } + + return $collection; + } + + /** + * Save a new version of a row. + * + * @param ModelInterface $model The model for which a new version shall be created. + * @param string $username The username to attach to the version as creator. + * + * @return void + * + * @throws \Doctrine\DBAL\Exception + */ + public function saveVersion(ModelInterface $model, $username) + { + $queryBuilder = $this->connection->createQueryBuilder(); + $queryBuilder->select('COUNT(*) AS count'); + $queryBuilder->from('tl_version'); + $queryBuilder->andWhere($queryBuilder->expr()->eq('tl_version.pid', ':pid')); + $queryBuilder->setParameter('pid', $model->getId()); + $queryBuilder->andWhere($queryBuilder->expr()->eq('tl_version.fromTable', ':fromTable')); + $queryBuilder->setParameter('fromTable', $this->source); + + $statement = $queryBuilder->executeQuery(); + $count = $statement->fetchFirstColumn(); + + $mixNewVersion = ((int) $count + 1); + $mixData = $model->getPropertiesAsArray(); + + $mixData[$this->idProperty] = $model->getId(); + + $insert = [ + 'tl_version.pid' => $model->getId(), + 'tl_version.tstamp' => \time(), + 'tl_version.version' => $mixNewVersion, + 'tl_version.fromTable' => $this->source, + 'tl_version.username' => $username, + 'tl_version.data' => \serialize($mixData) + ]; + + $this->connection->insert('tl_version', $insert); + + $this->setVersionActive($model->getId(), $mixNewVersion); + } + + /** + * Set a version as active. + * + * @param mixed $mixID The ID of the row. + * @param mixed $mixVersion The version number to set active. + * + * @return void + */ + public function setVersionActive($mixID, $mixVersion) + { + $updateValues = ['tl_version.pid' => $mixID, 'tl_version.fromTable' => $this->source]; + + // Set version inactive. + $this->connection->update('tl_version', ['tl_version.active' => ''], $updateValues); + + // Set version active. + $updateValues['version'] = $mixVersion; + $this->connection->update('tl_version', ['tl_version.active' => 1], $updateValues); + } + + /** + * Retrieve the current active version for a row. + * + * @param mixed $mixID The ID of the row. + * + * @return mixed The current version number of the requested row. + * + * @throws \Doctrine\DBAL\Exception + */ + public function getActiveVersion($mixID) + { + $queryBuilder = $this->connection->createQueryBuilder(); + $queryBuilder->select('version'); + $queryBuilder->from('tl_version'); + $queryBuilder->andWhere($queryBuilder->expr()->eq('tl_version.pid', ':pid')); + $queryBuilder->setParameter('pid', $mixID); + $queryBuilder->andWhere($queryBuilder->expr()->eq('tl_version.fromTable', ':fromTable')); + $queryBuilder->setParameter('fromTable', $this->source); + $queryBuilder->andWhere($queryBuilder->expr()->eq('tl_version.active', ':active')); + $queryBuilder->setParameter('active', 1); + + $statement = $queryBuilder->executeQuery(); + if (0 === $statement->rowCount()) { + return null; + } + + return $statement->fetchAllAssociative()['version']; + } + + /** + * Check if two models have the same values in all properties. + * + * @param ModelInterface $firstModel The first model to compare. + * @param ModelInterface $secondModel The second model to compare. + * + * @return boolean True - If both models are same, false if not. + */ + public function sameModels($firstModel, $secondModel) + { + foreach ($firstModel as $key => $value) { + if ($key === $this->idProperty) { + continue; + } + + if (\is_array($value)) { + if (!\is_array($secondModel->getProperty($key))) { + return false; + } + + if (\serialize($value) !== \serialize($secondModel->getProperty($key))) { + return false; + } + } elseif ($value !== $secondModel->getProperty($key)) { + return false; + } + } + + return true; + } + + /** + * Store an undo entry in the table tl_undo. + * + * Currently, this only supports delete queries. + * + * @param string $sourceSQL The SQL used to perform the action to be undone. + * @param string $saveSQL The SQL query to retrieve the current entries. + * @param string $table The table to be affected by the action. + * + * @return void + * @throws \Doctrine\DBAL\Exception + */ + protected function insertUndo($sourceSQL, $saveSQL, $table) + { + // Load row. + $statement = $this->connection->executeQuery($saveSQL); + + // Check if we have a result. + if (0 === $statement->rowCount()) { + return; + } + + $result = $statement->fetchAssociative(); + + // Save information in array. + $parameters = []; + foreach ($result as $value) { + $parameters[$table][] = $value; + } + + $prefix = '(DC General)'; + $user = BackendUser::getInstance(); + + // Write into undo. + $this->connection->insert( + 'tl_undo', + [ + 'tl_undo.pid' => $user->id, + 'tl_undo.tstamp' => \time(), + 'tl_undo.fromTable' => $table, + 'tl_undo.query' => $prefix . $sourceSQL, + 'tl_undo.affectedRows' => \count($parameters[$table]), + 'tl_undo.data' => \serialize($parameters) + ] + ); + } + + /** + * Get the default connection for the database. + * + * @return Connection + */ + protected function getDefaultConnection() + { + return System::getContainer()->get('database_connection'); + } + + /** + * This is a fallback for get the connection instead of the old database. + * + * @param array $config The configuration. + * + * @return void + * + * @deprecated This method is deprecated since 2.1 and where removed in 3.0. The old database never used in future. + */ + private function fallbackFromDatabaseToConnection(array &$config) + { + if (isset($config['database'])) { + // @codingStandardsIgnoreStart + @\trigger_error( + 'Config key database is deprecated use instead connection. Fallback will be dropped.', + E_USER_DEPRECATED + ); + // @codingStandardsIgnoreEnd + + if (!isset($config['connection'])) { + $config['connection'] = $config['database']; + } + + unset($config['database']); + } + + if (isset($config['connection']) && $config['connection'] instanceof Database) { + // @codingStandardsIgnoreStart + @\trigger_error( + '"' . __METHOD__ . '" now accepts doctrine instances - ' . + 'passing Contao database instances is deprecated.', + E_USER_DEPRECATED + ); + // @codingStandardsIgnoreEnd + $reflection = new \ReflectionProperty(Database::class, 'resConnection'); + $reflection->setAccessible(true); + + $config['connection'] = $reflection->getValue($config['connection']); + } + } +} diff --git a/src/Data/DefaultFilterOptionCollection.php b/src/Data/DefaultFilterOptionCollection.php index 48ea3218..eb99ef0e 100644 --- a/src/Data/DefaultFilterOptionCollection.php +++ b/src/Data/DefaultFilterOptionCollection.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Stefan Heimes * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -58,7 +59,7 @@ public function getIterator() * * @return int */ - public function count() + public function count(): int { return \count($this->filterValues); } diff --git a/src/Data/DefaultLanguageInformationCollection.php b/src/Data/DefaultLanguageInformationCollection.php index 8c6ffa70..178b44ec 100644 --- a/src/Data/DefaultLanguageInformationCollection.php +++ b/src/Data/DefaultLanguageInformationCollection.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,8 @@ * @package contao-community-alliance/dc-general * @author Christian Schiffler * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -57,7 +58,7 @@ public function getIterator() * * @return int */ - public function count() + public function count(): int { return \count($this->languages); } diff --git a/src/Data/PropertyValueBag.php b/src/Data/PropertyValueBag.php index 01fee7b3..64740cc1 100644 --- a/src/Data/PropertyValueBag.php +++ b/src/Data/PropertyValueBag.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2022 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -15,7 +15,7 @@ * @author Tristan Lins * @author Sven Baumann * @author Ingolf Steinhardt - * @copyright 2013-2022 Contao Community Alliance. + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -222,7 +222,7 @@ public function getIterator() /** * {@inheritdoc} */ - public function count() + public function count(): int { return \count($this->properties); } @@ -230,7 +230,7 @@ public function count() /** * {@inheritdoc} */ - public function offsetExists($offset) + public function offsetExists($offset): bool { return $this->hasPropertyValue($offset); } diff --git a/src/DataDefinition/Definition/DefaultDataProviderDefinition.php b/src/DataDefinition/Definition/DefaultDataProviderDefinition.php index d872f453..7761c5f6 100644 --- a/src/DataDefinition/Definition/DefaultDataProviderDefinition.php +++ b/src/DataDefinition/Definition/DefaultDataProviderDefinition.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -137,7 +138,7 @@ public function getIterator() /** * {@inheritdoc} */ - public function count() + public function count(): int { return \count($this->information); } diff --git a/src/DataDefinition/Definition/View/DefaultGroupAndSortingInformation.php b/src/DataDefinition/Definition/View/DefaultGroupAndSortingInformation.php index 4cd10eee..4e2ba6ef 100644 --- a/src/DataDefinition/Definition/View/DefaultGroupAndSortingInformation.php +++ b/src/DataDefinition/Definition/View/DefaultGroupAndSortingInformation.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,8 @@ * @package contao-community-alliance/dc-general * @author Christian Schiffler * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -109,7 +110,7 @@ public function setGroupingLength($value) /** * {@inheritDoc} */ - public function getGroupingLength() + public function getGroupingLength(): int { return $this->groupingLength; } diff --git a/src/DataDefinition/ModelRelationship/FilterBuilder/FilterBuilderWithChildren.php b/src/DataDefinition/ModelRelationship/FilterBuilder/FilterBuilderWithChildren.php index ebf00141..8e27c64d 100644 --- a/src/DataDefinition/ModelRelationship/FilterBuilder/FilterBuilderWithChildren.php +++ b/src/DataDefinition/ModelRelationship/FilterBuilder/FilterBuilderWithChildren.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2034 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,8 @@ * @package contao-community-alliance/dc-general * @author Christian Schiffler * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2034 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -97,7 +98,7 @@ public function __clone() * * @throws DcGeneralRuntimeException When the current position is invalid. */ - public function current() + public function current(): ?BaseFilterBuilder { if (-1 === $this->index) { return $this->first(); @@ -115,7 +116,7 @@ public function current() * * @return BaseFilterBuilder */ - public function next() + public function next(): ?BaseFilterBuilder { $this->index++; @@ -137,7 +138,7 @@ public function key() * * @return boolean Returns true on success or false on failure. */ - public function valid() + public function valid(): bool { return ($this->index > -1) && ($this->index < \count($this->children)); } @@ -149,7 +150,7 @@ public function valid() * * @return BaseFilterBuilder */ - public function rewind() + public function rewind(): ?BaseFilterBuilder { return $this->first(); } @@ -177,7 +178,7 @@ public function first() * * @return boolean true on success or false on failure. */ - public function offsetExists($offset) + public function offsetExists($offset): bool { return isset($this->children[$offset]); } @@ -189,7 +190,7 @@ public function offsetExists($offset) * * @return BaseFilterBuilder */ - public function offsetGet($offset) + public function offsetGet($offset): BaseFilterBuilder { return $this->children[$offset]; } @@ -202,7 +203,7 @@ public function offsetGet($offset) * * @return FilterBuilderWithChildren The current builder. */ - public function offsetSet($offset, $value) + public function offsetSet($offset, $value): FilterBuilderWithChildren { $this->children[$offset] = $value; @@ -216,7 +217,7 @@ public function offsetSet($offset, $value) * * @return FilterBuilderWithChildren The current builder. */ - public function offsetUnset($offset) + public function offsetUnset($offset): FilterBuilderWithChildren { unset($this->children[$offset]); diff --git a/src/Panel/DefaultPanel.php b/src/Panel/DefaultPanel.php index e172f604..7ea68984 100644 --- a/src/Panel/DefaultPanel.php +++ b/src/Panel/DefaultPanel.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -109,7 +110,7 @@ public function getIterator() /** * {@inheritdoc} */ - public function count() + public function count(): int { return \count($this->arrElements); } diff --git a/src/Panel/DefaultPanelContainer.php b/src/Panel/DefaultPanelContainer.php index 81027cf0..4bda3f35 100644 --- a/src/Panel/DefaultPanelContainer.php +++ b/src/Panel/DefaultPanelContainer.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -109,7 +110,7 @@ public function getIterator() /** * {@inheritdoc} */ - public function count() + public function count(): int { return \count($this->arrPanels); } From 7d10ed5463dceadf3fdbbe1d7720743b3ee29b47 Mon Sep 17 00:00:00 2001 From: Ingolf Steinhardt Date: Wed, 6 Sep 2023 21:48:28 +0200 Subject: [PATCH 39/53] Any fixes --- .composer-require-checker.json | 1 - composer.json | 1 + .../Dca/Populator/BackendViewPopulator.php | 58 ++++++++++++-- src/Contao/Event/Subscriber.php | 2 + .../AbstractListShowAllHandler.php | 76 ++++++++++++++++--- .../ActionHandler/CopyHandler.php | 7 +- .../ActionHandler/DeleteHandler.php | 7 +- .../ActionHandler/EditHandler.php | 6 +- .../ActionHandler/SelectHandler.php | 6 +- .../ActionHandler/ShowHandler.php | 2 +- .../View/Contao2BackendView/EditMask.php | 10 +-- .../Subscriber/GetGroupHeaderSubscriber.php | 2 + .../View/Contao2BackendView/TreeView.php | 55 ++++++++++++++ src/Controller/Ajax3X.php | 8 +- .../Palette/Builder/Event/BuilderEvent.php | 7 +- .../config/contao/backend_event_listeners.yml | 48 ++++++------ .../contao/handler_backend_listeners.yml | 4 + .../handler_multiple_backend_listeners.yml | 2 + ...AbstractPropertyOverrideEditAllHandler.php | 6 +- 19 files changed, 238 insertions(+), 70 deletions(-) diff --git a/.composer-require-checker.json b/.composer-require-checker.json index 7a3eff87..ae355952 100644 --- a/.composer-require-checker.json +++ b/.composer-require-checker.json @@ -1,7 +1,6 @@ { "symbol-whitelist": [ "array", "bool", "false", "int", "mixed", "null", "self", "static", "parent", "string", "true", "void", - "ampersand", "array_insert", "array_is_assoc", "nl2br_html5", "TL_ERROR", "ContaoTwigInitializeEvent", "ContaoCommunityAlliance\\DcGeneral\\Contao\\View\\Contao2BackendView\\Exception\\EditOnlyModeException", "ContaoCommunityAlliance\\DcGeneral\\Contao\\View\\Contao2BackendView\\Exception\\NotCreatableException", diff --git a/composer.json b/composer.json index bb0eed9a..bd34f8f7 100644 --- a/composer.json +++ b/composer.json @@ -56,6 +56,7 @@ "symfony/polyfill-mbstring": "^1.0", "symfony/routing": "^5.4", "symfony/security-core": "^5.4", + "symfony/security-csrf": "^5.4", "symfony/translation-contracts": "^2.5", "twig/twig": "^3.0" }, diff --git a/src/Contao/Dca/Populator/BackendViewPopulator.php b/src/Contao/Dca/Populator/BackendViewPopulator.php index 0249476b..714ae82e 100644 --- a/src/Contao/Dca/Populator/BackendViewPopulator.php +++ b/src/Contao/Dca/Populator/BackendViewPopulator.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -15,13 +15,15 @@ * @author Tristan Lins * @author David Molineus * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ namespace ContaoCommunityAlliance\DcGeneral\Contao\Dca\Populator; +use Contao\System; use ContaoCommunityAlliance\DcGeneral\Contao\DataDefinition\Definition\Contao2BackendViewDefinitionInterface; use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminator; use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminatorAwareTrait; @@ -34,6 +36,7 @@ use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\BasicDefinitionInterface; use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralInvalidArgumentException; +use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface; /** * This class is the default fallback populator in the Contao Backend to instantiate a BackendView. @@ -44,13 +47,56 @@ class BackendViewPopulator extends AbstractEventDrivenBackendEnvironmentPopulato { use RequestScopeDeterminatorAwareTrait; + /** + * The token manager. + * + * @var CsrfTokenManagerInterface + */ + private CsrfTokenManagerInterface $tokenManager; + + /** + * The token name. + * + * @var string + */ + private string $tokenName; + /** * BackendViewPopulator constructor. * - * @param RequestScopeDeterminator $scopeDeterminator The request mode determinator. + * @param RequestScopeDeterminator $scopeDeterminator The request mode determinator. + * @param CsrfTokenManagerInterface|null $tokenManager The token manager. + * @param string|null $tokenName The token name. */ - public function __construct(RequestScopeDeterminator $scopeDeterminator) - { + public function __construct( + RequestScopeDeterminator $scopeDeterminator, + ?CsrfTokenManagerInterface $tokenManager = null, + ?string $tokenName = null + ) { + if (null === $tokenManager) { + $tokenManager = System::getContainer()->get('security.csrf.token_manager'); + // @codingStandardsIgnoreStart + @trigger_error( + 'Not passing the csrf token manager as 2th argument to "' . __METHOD__ . '" is deprecated ' . + 'and will cause an error in DCG 3.0', + E_USER_DEPRECATED + ); + // @codingStandardsIgnoreEnd + } + if (null === $tokenName) { + $tokenName = System::getContainer()->getParameter('contao.csrf_token_name'); + // @codingStandardsIgnoreStart + @trigger_error( + 'Not passing the csrf token name as 3th argument to "' . __METHOD__ . '" is deprecated ' . + 'and will cause an error in DCG 3.0', + E_USER_DEPRECATED + ); + // @codingStandardsIgnoreEnd + } + + $this->tokenManager = $tokenManager; + $this->tokenName = $tokenName; + $this->setScopeDeterminator($scopeDeterminator); } @@ -86,7 +132,7 @@ protected function populateView(EnvironmentInterface $environment) $view = new ParentView($this->scopeDeterminator); break; case BasicDefinitionInterface::MODE_HIERARCHICAL: - $view = new TreeView($this->scopeDeterminator); + $view = new TreeView($this->scopeDeterminator, $this->tokenManager, $this->tokenName); break; default: $mode = $dataDefinition->getBasicDefinition()->getMode(); diff --git a/src/Contao/Event/Subscriber.php b/src/Contao/Event/Subscriber.php index 05dfe072..2fcfb1cc 100644 --- a/src/Contao/Event/Subscriber.php +++ b/src/Contao/Event/Subscriber.php @@ -293,6 +293,8 @@ public function renderReadablePropertyValue(RenderReadablePropertyValueEvent $ev * @param \ContaoTwigInitializeEvent $event The event. * * @return void + * + * @psalm-suppress UndefinedClass */ public function initTwig(\ContaoTwigInitializeEvent $event): void { diff --git a/src/Contao/View/Contao2BackendView/ActionHandler/AbstractListShowAllHandler.php b/src/Contao/View/Contao2BackendView/ActionHandler/AbstractListShowAllHandler.php index bb0837bb..12f30709 100644 --- a/src/Contao/View/Contao2BackendView/ActionHandler/AbstractListShowAllHandler.php +++ b/src/Contao/View/Contao2BackendView/ActionHandler/AbstractListShowAllHandler.php @@ -27,6 +27,7 @@ use Contao\Environment; use Contao\Message; use Contao\StringUtil; +use Contao\System; use ContaoCommunityAlliance\Contao\Bindings\ContaoEvents; use ContaoCommunityAlliance\Contao\Bindings\Events\Backend\AddToUrlEvent; use ContaoCommunityAlliance\Contao\Bindings\Events\Image\GenerateHtmlEvent; @@ -59,8 +60,17 @@ use ContaoCommunityAlliance\DcGeneral\Event\ViewEvent; use ContaoCommunityAlliance\DcGeneral\View\ActionHandler\CallActionTrait; use ContaoCommunityAlliance\Translator\TranslatorInterface as CcaTranslator; +use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface; use Symfony\Contracts\Translation\TranslatorInterface; +use function array_key_exists; +use function implode; +use function in_array; +use function sprintf; +use function str_replace; +use function strpos; +use function trigger_error; + /** * This class is the abstract base for parent list and plain list "showAll" commands. * @@ -77,7 +87,7 @@ abstract class AbstractListShowAllHandler * * @var TranslatorInterface */ - protected $translator; + protected TranslatorInterface $translator; /** * The cca translator. @@ -86,22 +96,64 @@ abstract class AbstractListShowAllHandler */ private $ccaTranslator; + /** + * The token manager. + * + * @var CsrfTokenManagerInterface + */ + private CsrfTokenManagerInterface $tokenManager; + + /** + * The token name. + * + * @var string + */ + private string $tokenName; + /** * AbstractHandler constructor. * * @param RequestScopeDeterminator $scopeDeterminator The request mode determinator. * @param TranslatorInterface $translator The translator. * @param CcaTranslator|TranslatorInterface $ccaTranslator The cca translator. + * @param CsrfTokenManagerInterface|null $tokenManager The token manager. + * @param string|null $tokenName The token name. */ public function __construct( RequestScopeDeterminator $scopeDeterminator, TranslatorInterface $translator, - CcaTranslator $ccaTranslator + CcaTranslator $ccaTranslator, + ?CsrfTokenManagerInterface $tokenManager = null, + ?string $tokenName = null ) { $this->setScopeDeterminator($scopeDeterminator); $this->translator = $translator; $this->ccaTranslator = $ccaTranslator; + + if (null === $tokenManager) { + $tokenManager = System::getContainer()->get('security.csrf.token_manager'); + // @codingStandardsIgnoreStart + @trigger_error( + 'Not passing the csrf token manager as 4th argument to "' . __METHOD__ . '" is deprecated ' . + 'and will cause an error in DCG 3.0', + E_USER_DEPRECATED + ); + // @codingStandardsIgnoreEnd + } + if (null === $tokenName) { + $tokenName = System::getContainer()->getParameter('contao.csrf_token_name'); + // @codingStandardsIgnoreStart + @trigger_error( + 'Not passing the csrf token name as 5th argument to "' . __METHOD__ . '" is deprecated ' . + 'and will cause an error in DCG 3.0', + E_USER_DEPRECATED + ); + // @codingStandardsIgnoreEnd + } + + $this->tokenManager = $tokenManager; + $this->tokenName = $tokenName; } /** @@ -167,7 +219,7 @@ protected function process(Action $action, EnvironmentInterface $environment) $clipboard = new ViewEvent($environment, $action, DcGeneralViews::CLIPBOARD, []); $environment->getEventDispatcher()->dispatch($clipboard, DcGeneralEvents::VIEW); - return \implode( + return implode( "\n", [ 'language' => $this->languageSwitcher($environment), @@ -201,7 +253,7 @@ private function languageSwitcher(EnvironmentInterface $environment) ->set('languages', $environment->getController()->getSupportedLanguages(null)) ->set('language', $dataProvider->getCurrentLanguage()) ->set('submit', $this->translator->trans('MSC.showSelected', [], 'contao_default')) - ->set('REQUEST_TOKEN', REQUEST_TOKEN) + ->set('REQUEST_TOKEN', $this->tokenManager->getToken($this->tokenName)) ->parse(); } @@ -243,7 +295,7 @@ protected function translate($key, $domain, array $parameters = []) // Fallback translate for non symfony domain. if ($translated === $key) { // @codingStandardsIgnoreStart - @\trigger_error( + @trigger_error( 'Fallback translation for contao lang in the global array. ' . 'This will remove in the future, use the symfony domain translation.', E_USER_DEPRECATED @@ -251,7 +303,7 @@ protected function translate($key, $domain, array $parameters = []) // @codingStandardsIgnoreEnd $translated = - $this->translator->trans(\sprintf('%s.%s', $domain, $key), $parameters, sprintf('contao_%s', $domain)); + $this->translator->trans(sprintf('%s.%s', $domain, $key), $parameters, sprintf('contao_%s', $domain)); } return $translated; @@ -325,9 +377,9 @@ protected function renderTemplate(ContaoBackendViewTemplate $template, Environme if ( (null !== $template->get('action')) - && (false !== \strpos($template->get('action'), 'select=models')) + && (false !== strpos($template->get('action'), 'select=models')) ) { - $template->set('action', \str_replace('select=models', 'select=properties', $template->get('action'))); + $template->set('action', str_replace('select=models', 'select=properties', $template->get('action'))); } } @@ -403,7 +455,7 @@ private function renderCollection(EnvironmentInterface $environment, CollectionI $cssClasses[] = 'tl_folder_clipped'; } - $model->setMeta($model::CSS_ROW_CLASS, \implode(' ', $cssClasses)); + $model->setMeta($model::CSS_ROW_CLASS, implode(' ', $cssClasses)); $this->renderModel($model, $environment); } @@ -484,7 +536,7 @@ private function getTableHead(EnvironmentInterface $environment) $columns = $this->getSortingColumns($sorting); foreach ($formatter->getPropertyNames() as $field) { $tableHead[] = [ - 'class' => 'tl_folder_tlist col_' . $field . (\in_array($field, $columns) ? ' ordered_by' : ''), + 'class' => 'tl_folder_tlist col_' . $field . (in_array($field, $columns) ? ' ordered_by' : ''), 'content' => $properties->hasProperty($field) ? $properties->getProperty($field)->getLabel() : $this->translate($definition->getName() . '.' . $field . '.0', 'contao_' . $definition->getName()) @@ -602,7 +654,7 @@ protected function renderPasteTopButton(EnvironmentInterface $environment, $sort ContaoEvents::IMAGE_GET_HTML ); - return \sprintf( + return sprintf( '%s', $urlEvent->getUrl(), StringUtil::specialchars($this->translate('pasteafter.0', $languageDomain)), @@ -685,7 +737,7 @@ private function getSelectContainer(EnvironmentInterface $environment) } $session = $sessionStorage->get($sessionName); - if (!\array_key_exists($selectAction, $session)) { + if (!array_key_exists($selectAction, $session)) { return []; } diff --git a/src/Contao/View/Contao2BackendView/ActionHandler/CopyHandler.php b/src/Contao/View/Contao2BackendView/ActionHandler/CopyHandler.php index 6aa2be29..e0b54933 100644 --- a/src/Contao/View/Contao2BackendView/ActionHandler/CopyHandler.php +++ b/src/Contao/View/Contao2BackendView/ActionHandler/CopyHandler.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2021 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -16,7 +16,8 @@ * @author Stefan Heimes * @author Sven Baumann * @author Richard Henkenjohann - * @copyright 2013-2021 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -136,7 +137,7 @@ protected function guardIsCreatable(EnvironmentInterface $environment, ModelIdIn $dataDefinition->getName() ), __CLASS__ . '::delete()', - TL_ERROR + 'ERROR' ), ContaoEvents::SYSTEM_LOG ); diff --git a/src/Contao/View/Contao2BackendView/ActionHandler/DeleteHandler.php b/src/Contao/View/Contao2BackendView/ActionHandler/DeleteHandler.php index fb2edfb2..26fca809 100644 --- a/src/Contao/View/Contao2BackendView/ActionHandler/DeleteHandler.php +++ b/src/Contao/View/Contao2BackendView/ActionHandler/DeleteHandler.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2021 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -15,7 +15,8 @@ * @author Christian Schiffler * @author Stefan Heimes * @author Sven Baumann - * @copyright 2013-2021 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -124,7 +125,7 @@ protected function guardIsDeletable(EnvironmentInterface $environment, ModelIdIn $dataDefinition->getName() ), __CLASS__ . '::delete()', - TL_ERROR + 'ERROR' ), ContaoEvents::SYSTEM_LOG ); diff --git a/src/Contao/View/Contao2BackendView/ActionHandler/EditHandler.php b/src/Contao/View/Contao2BackendView/ActionHandler/EditHandler.php index 124d4889..3e9de8e4 100644 --- a/src/Contao/View/Contao2BackendView/ActionHandler/EditHandler.php +++ b/src/Contao/View/Contao2BackendView/ActionHandler/EditHandler.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2022 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -15,7 +15,7 @@ * @author Sven Baumann * @author David Molineus * @author Ingolf Steinhardt - * @copyright 2013-2022 Contao Community Alliance. + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -200,7 +200,7 @@ private function checkRestoreVersion(EnvironmentInterface $environment, ModelId ); $environment->getEventDispatcher()->dispatch( - new LogEvent($message, TL_ERROR, 'DC_General - checkRestoreVersion()'), + new LogEvent($message, 'ERROR', 'DC_General - checkRestoreVersion()'), ContaoEvents::SYSTEM_LOG ); diff --git a/src/Contao/View/Contao2BackendView/ActionHandler/SelectHandler.php b/src/Contao/View/Contao2BackendView/ActionHandler/SelectHandler.php index 3b31d54d..ce4ca628 100644 --- a/src/Contao/View/Contao2BackendView/ActionHandler/SelectHandler.php +++ b/src/Contao/View/Contao2BackendView/ActionHandler/SelectHandler.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2022 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -16,7 +16,7 @@ * @author Stefan Heimes * @author Sven Baumann * @author Ingolf Steinhardt - * @copyright 2013-2022 Contao Community Alliance. + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -549,7 +549,7 @@ private function getSelectCollection(EnvironmentInterface $environment) $sessionStorage->get($dataDefinition->getName() . '.' . $this->getSubmitAction($environment, true)); $modelIds = []; - foreach ($session['models'] as $modelId) { + foreach (($session['models'] ?? []) as $modelId) { $modelIds[] = ModelId::fromSerialized($modelId)->getId(); } diff --git a/src/Contao/View/Contao2BackendView/ActionHandler/ShowHandler.php b/src/Contao/View/Contao2BackendView/ActionHandler/ShowHandler.php index 2267b179..7f6f9ee9 100644 --- a/src/Contao/View/Contao2BackendView/ActionHandler/ShowHandler.php +++ b/src/Contao/View/Contao2BackendView/ActionHandler/ShowHandler.php @@ -105,7 +105,7 @@ protected function getModel(EnvironmentInterface $environment) $environment->getDataDefinition()->getName() ), __CLASS__ . '::' . __FUNCTION__, - TL_ERROR + 'ERROR' ), ContaoEvents::SYSTEM_LOG ); diff --git a/src/Contao/View/Contao2BackendView/EditMask.php b/src/Contao/View/Contao2BackendView/EditMask.php index f43b4c2c..2a4704f8 100644 --- a/src/Contao/View/Contao2BackendView/EditMask.php +++ b/src/Contao/View/Contao2BackendView/EditMask.php @@ -201,7 +201,7 @@ protected function checkEditable($model) if ($model->getId() && !$definition->getBasicDefinition()->isEditable()) { $message = 'DataContainer ' . $definition->getName() . ' is not editable'; $environment->getEventDispatcher()->dispatch( - new LogEvent($message, TL_ERROR, 'DC_General - edit()'), + new LogEvent($message, 'ERROR', 'DC_General - edit()'), ContaoEvents::SYSTEM_LOG ); throw new DcGeneralRuntimeException($message); @@ -222,11 +222,11 @@ protected function checkCreatable($model) $environment = $this->getEnvironment(); $definition = $this->getDataDefinition(); - // Check if table is closed but we are adding a new item. + // Check if table is closed, but we are adding a new item. if (!($model->getId() || $definition->getBasicDefinition()->isCreatable())) { $message = 'DataContainer ' . $definition->getName() . ' is closed'; $environment->getEventDispatcher()->dispatch( - new LogEvent($message, TL_ERROR, 'DC_General - edit()'), + new LogEvent($message, 'ERROR', 'DC_General - edit()'), ContaoEvents::SYSTEM_LOG ); throw new DcGeneralRuntimeException($message); @@ -660,13 +660,13 @@ protected function handleSubmit(ModelInterface $model) * * @deprecated This is deprecated since 2.3 and will be removed in 3.0. */ - private function getHeadline(): string + protected function getHeadline(): string { // @codingStandardsIgnoreStart @\trigger_error(__CLASS__ . '::' . __METHOD__ . ' is deprecated - use getSubHeadline()!', E_USER_DEPRECATED); // @codingStandardsIgnoreEnd - $this->getSubHeadline(); + return $this->getSubHeadline(); } /** diff --git a/src/Contao/View/Contao2BackendView/Subscriber/GetGroupHeaderSubscriber.php b/src/Contao/View/Contao2BackendView/Subscriber/GetGroupHeaderSubscriber.php index 7426536d..2ecf4d88 100644 --- a/src/Contao/View/Contao2BackendView/Subscriber/GetGroupHeaderSubscriber.php +++ b/src/Contao/View/Contao2BackendView/Subscriber/GetGroupHeaderSubscriber.php @@ -39,6 +39,8 @@ /** * Handles the group header formatting. + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class GetGroupHeaderSubscriber { diff --git a/src/Contao/View/Contao2BackendView/TreeView.php b/src/Contao/View/Contao2BackendView/TreeView.php index 946db1e5..884cfefd 100644 --- a/src/Contao/View/Contao2BackendView/TreeView.php +++ b/src/Contao/View/Contao2BackendView/TreeView.php @@ -27,11 +27,13 @@ use Contao\Backend; use Contao\CoreBundle\Exception\ResponseException; use Contao\StringUtil; +use Contao\System; use ContaoCommunityAlliance\Contao\Bindings\ContaoEvents; use ContaoCommunityAlliance\Contao\Bindings\Events\Backend\AddToUrlEvent; use ContaoCommunityAlliance\Contao\Bindings\Events\Image\GenerateHtmlEvent; use ContaoCommunityAlliance\DcGeneral\Action; use ContaoCommunityAlliance\DcGeneral\Clipboard\Filter; +use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminator; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\GetPasteRootButtonEvent; use ContaoCommunityAlliance\DcGeneral\Controller\ModelCollector; use ContaoCommunityAlliance\DcGeneral\Controller\TreeCollector; @@ -51,6 +53,7 @@ use ContaoCommunityAlliance\DcGeneral\Panel\LimitElementInterface; use ContaoCommunityAlliance\DcGeneral\Panel\SortElementInterface; use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface; /** * Class TreeView. @@ -62,6 +65,58 @@ */ class TreeView extends BaseView { + /** + * The token manager. + * + * @var CsrfTokenManagerInterface + */ + private CsrfTokenManagerInterface $tokenManager; + + /** + * The token name. + * + * @var string + */ + private string $tokenName; + + /** + * TreeView constructor. + * + * @param CsrfTokenManagerInterface|null $tokenManager The token manager. + * @param string|null $tokenName The token name. + */ + public function __construct( + RequestScopeDeterminator $scopeDeterminator, + ?CsrfTokenManagerInterface $tokenManager = null, + ?string $tokenName = null + ) { + parent::__construct($scopeDeterminator); + + if (null === $tokenManager) { + $tokenManager = System::getContainer()->get('security.csrf.token_manager'); + // @codingStandardsIgnoreStart + @trigger_error( + 'Not passing the csrf token manager as 2th argument to "' . __METHOD__ . '" is deprecated ' . + 'and will cause an error in DCG 3.0', + E_USER_DEPRECATED + ); + // @codingStandardsIgnoreEnd + } + if (null === $tokenName) { + $tokenName = System::getContainer()->getParameter('contao.csrf_token_name'); + // @codingStandardsIgnoreStart + @trigger_error( + 'Not passing the csrf token name as 3th argument to "' . __METHOD__ . '" is deprecated ' . + 'and will cause an error in DCG 3.0', + E_USER_DEPRECATED + ); + // @codingStandardsIgnoreEnd + } + + $this->tokenManager = $tokenManager; + $this->tokenName = $tokenName; + } + /** * Retrieve the id for this view. * diff --git a/src/Controller/Ajax3X.php b/src/Controller/Ajax3X.php index 3fc527f1..e244b7e6 100644 --- a/src/Controller/Ajax3X.php +++ b/src/Controller/Ajax3X.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2021 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -17,7 +17,8 @@ * @author Andreas Nölke * @author David Molineus * @author Sven Baumann - * @copyright 2013-2021 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -123,7 +124,6 @@ protected function loadPagetree() $widget = new $GLOBALS['BE_FFL']['pageSelector']($arrData, $this->getDataContainer()); $widget->value = $this->getTreeValue('page', $input->getValue('value')); - $response = new Response($widget->generateAjax($ajaxId, $field, $level)); throw new ResponseException($response); @@ -216,7 +216,7 @@ protected function getModelFromSerializedId($serializedId) 'A record with the ID "' . $serializedId . '" does not exist in "' . $this->getEnvironment()->getDataDefinition()->getName() . '"', 'Ajax executePostActions()', - TL_ERROR + 'ERROR' ); $this->getEnvironment()->getEventDispatcher()->dispatch($event, ContaoEvents::SYSTEM_LOG); diff --git a/src/DataDefinition/Palette/Builder/Event/BuilderEvent.php b/src/DataDefinition/Palette/Builder/Event/BuilderEvent.php index 30ec4d03..de13da7e 100644 --- a/src/DataDefinition/Palette/Builder/Event/BuilderEvent.php +++ b/src/DataDefinition/Palette/Builder/Event/BuilderEvent.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -31,6 +32,8 @@ */ abstract class BuilderEvent extends AbstractContainerAwareEvent { + public const NAME = 'dc-general.data-definition.palette.builder.builder'; + /** * The palette builder in use. * diff --git a/src/Resources/config/contao/backend_event_listeners.yml b/src/Resources/config/contao/backend_event_listeners.yml index 4762d9dd..e8af31e4 100644 --- a/src/Resources/config/contao/backend_event_listeners.yml +++ b/src/Resources/config/contao/backend_event_listeners.yml @@ -1,27 +1,27 @@ services: - cca.dc-general.backend_listener.format_model_label_subscriber: - class: ContaoCommunityAlliance\DcGeneral\Contao\Subscriber\FormatModelLabelSubscriber - public: true - calls: - - method: setScopeDeterminator - arguments: - - "@cca.dc-general.scope-matcher" - tags: - - name: kernel.event_listener - event: dc-general.model.format_model_label - method: handleFormatModelLabel + cca.dc-general.backend_listener.format_model_label_subscriber: + class: ContaoCommunityAlliance\DcGeneral\Contao\Subscriber\FormatModelLabelSubscriber + public: true + calls: + - method: setScopeDeterminator + arguments: + - "@cca.dc-general.scope-matcher" + tags: + - name: kernel.event_listener + event: dc-general.model.format_model_label + method: handleFormatModelLabel - cca.dc-general.backend_listener.get_group_header_subscriber: - class: ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Subscriber\GetGroupHeaderSubscriber - public: true + cca.dc-general.backend_listener.get_group_header_subscriber: + class: ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Subscriber\GetGroupHeaderSubscriber + public: true + arguments: + - "@event_dispatcher" + - "@cca.translator.contao_translator" + calls: + - method: setScopeDeterminator arguments: - - "@event_dispatcher" - - "@cca.translator.contao_translator" - calls: - - method: setScopeDeterminator - arguments: - - "@cca.dc-general.scope-matcher" - tags: - - name: kernel.event_listener - event: dc-general.view.contao2backend.get-group-header - method: handle + - "@cca.dc-general.scope-matcher" + tags: + - name: kernel.event_listener + event: dc-general.view.contao2backend.get-group-header + method: handle diff --git a/src/Resources/config/contao/handler_backend_listeners.yml b/src/Resources/config/contao/handler_backend_listeners.yml index 1aa415c9..9cf8ff6f 100644 --- a/src/Resources/config/contao/handler_backend_listeners.yml +++ b/src/Resources/config/contao/handler_backend_listeners.yml @@ -69,6 +69,8 @@ services: - "@cca.dc-general.scope-matcher" - "@translator" - "@cca.translator.contao_translator" + - "@security.csrf.token_manager" + - "%contao.csrf_token_name%" tags: - name: kernel.event_listener event: dc-general.action @@ -81,6 +83,8 @@ services: - "@cca.dc-general.scope-matcher" - "@translator" - "@cca.translator.contao_translator" + - "@security.csrf.token_manager" + - "%contao.csrf_token_name%" tags: - name: kernel.event_listener event: dc-general.action diff --git a/src/Resources/config/contao/handler_multiple_backend_listeners.yml b/src/Resources/config/contao/handler_multiple_backend_listeners.yml index f7bf2fc5..ce6b8eb5 100644 --- a/src/Resources/config/contao/handler_multiple_backend_listeners.yml +++ b/src/Resources/config/contao/handler_multiple_backend_listeners.yml @@ -46,6 +46,8 @@ services: - "@cca.dc-general.scope-matcher" - "@translator" - "@cca.translator.contao_translator" + - "@security.csrf.token_manager" + - "%contao.csrf_token_name%" tags: - name: kernel.event_listener event: dc-general.action diff --git a/src/View/ActionHandler/AbstractPropertyOverrideEditAllHandler.php b/src/View/ActionHandler/AbstractPropertyOverrideEditAllHandler.php index 894cf1a6..a7ff7194 100644 --- a/src/View/ActionHandler/AbstractPropertyOverrideEditAllHandler.php +++ b/src/View/ActionHandler/AbstractPropertyOverrideEditAllHandler.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2022 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,7 @@ * @author Sven Baumann * @author Richard Henkenjohann * @author Ingolf Steinhardt - * @copyright 2013-2022 Contao Community Alliance. + * @copyright 2013-2023s Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0 * @filesource */ @@ -662,7 +662,7 @@ protected function renderBreadcrumb(EnvironmentInterface $environment) return null; } - $GLOBALS['TL_CSS']['cca.dc-general.breadcrumb'] = 'system/modules/dc-general/html/css/generalBreadcrumb.css'; + $GLOBALS['TL_CSS']['cca.dc-general.generalBreadcrumb'] = 'bundles/ccadcgeneral/css/generalBreadcrumb.css'; $template = new ContaoBackendViewTemplate('dcbe_general_breadcrumb'); $template->set('elements', $elements); From 26a3f003701c7139e8f7c5fbf167ae66da3948a5 Mon Sep 17 00:00:00 2001 From: Christian Schiffler Date: Wed, 6 Sep 2023 21:57:39 +0200 Subject: [PATCH 40/53] Fix more psalm issues --- .../Dca/Populator/BackendViewPopulator.php | 8 +- .../Dca/Populator/DataProviderPopulator.php | 2 +- .../Populator/ExtendedLegacyDcaPopulator.php | 2 +- src/Contao/Event/Subscriber.php | 53 +++- src/Contao/Factory/SessionStorageFactory.php | 12 +- .../Picker/AbstractAwarePickerProvider.php | 4 +- src/Contao/Picker/PagePickerProvider.php | 2 +- .../RequestScopeDeterminatorAwareTrait.php | 15 +- src/Contao/SessionStorage.php | 6 +- .../Subscriber/FormatModelLabelSubscriber.php | 2 +- .../AbstractListShowAllHandler.php | 2 +- .../ActionHandler/CopyHandler.php | 2 +- .../ActionHandler/CreateHandler.php | 2 +- .../ActionHandler/DeleteHandler.php | 2 +- .../ActionHandler/EditHandler.php | 2 +- .../MultipleHandler/EditAllHandler.php | 2 +- .../MultipleHandler/OverrideAllHandler.php | 2 +- .../MultipleHandler/PasteAllHandler.php | 2 +- .../MultipleHandler/SelectModelAllHandler.php | 2 +- .../SelectPropertyAllHandler.php | 2 +- .../ActionHandler/PasteHandler.php | 2 +- .../ActionHandler/SelectHandler.php | 2 +- .../ActionHandler/ToggleHandler.php | 2 +- .../View/Contao2BackendView/BaseView.php | 2 +- .../Contao2BackendView/ButtonRenderer.php | 3 + .../EventListener/BackButtonListener.php | 2 +- .../CreateModelButtonListener.php | 2 +- .../Subscriber/GetGroupHeaderSubscriber.php | 2 +- .../Subscriber/MultipleHandlerSubscriber.php | 8 +- src/DataDefinition/ContainerInterface.php | 4 +- src/DataDefinition/Palette/Legend.php | 4 +- .../Palette/LegendInterface.php | 15 +- .../Palette/PaletteCollection.php | 4 +- .../Palette/PaletteCollectionInterface.php | 7 +- ...bstractEventDrivenEnvironmentPopulator.php | 4 +- src/InputProviderInterface.php | 44 +-- src/View/ActionHandler/AbstractHandler.php | 35 ++- ...AbstractPropertyOverrideEditAllHandler.php | 38 ++- .../AbstractPropertyVisibilityHandler.php | 279 ++++++++++++------ 39 files changed, 387 insertions(+), 198 deletions(-) diff --git a/src/Contao/Dca/Populator/BackendViewPopulator.php b/src/Contao/Dca/Populator/BackendViewPopulator.php index 714ae82e..01a39f4e 100644 --- a/src/Contao/Dca/Populator/BackendViewPopulator.php +++ b/src/Contao/Dca/Populator/BackendViewPopulator.php @@ -126,13 +126,13 @@ protected function populateView(EnvironmentInterface $environment) switch ($dataDefinition->getBasicDefinition()->getMode()) { case BasicDefinitionInterface::MODE_FLAT: - $view = new ListView($this->scopeDeterminator); + $view = new ListView($this->getScopeDeterminator()); break; case BasicDefinitionInterface::MODE_PARENTEDLIST: - $view = new ParentView($this->scopeDeterminator); + $view = new ParentView($this->getScopeDeterminator()); break; case BasicDefinitionInterface::MODE_HIERARCHICAL: - $view = new TreeView($this->scopeDeterminator, $this->tokenManager, $this->tokenName); + $view = new TreeView($this->getScopeDeterminator(), $this->tokenManager, $this->tokenName); break; default: $mode = $dataDefinition->getBasicDefinition()->getMode(); @@ -186,7 +186,7 @@ protected function populatePanel(EnvironmentInterface $environment) */ public function populate(EnvironmentInterface $environment) { - if (!$this->scopeDeterminator->currentScopeIsBackend()) { + if (!$this->getScopeDeterminator()->currentScopeIsBackend()) { return; } diff --git a/src/Contao/Dca/Populator/DataProviderPopulator.php b/src/Contao/Dca/Populator/DataProviderPopulator.php index d10a3568..a035ef23 100644 --- a/src/Contao/Dca/Populator/DataProviderPopulator.php +++ b/src/Contao/Dca/Populator/DataProviderPopulator.php @@ -43,7 +43,7 @@ class DataProviderPopulator extends AbstractEventDrivenEnvironmentPopulator * * @var array */ - private $instances = []; + private array $instances = []; /** * Creates an instance of itself and processes the event. diff --git a/src/Contao/Dca/Populator/ExtendedLegacyDcaPopulator.php b/src/Contao/Dca/Populator/ExtendedLegacyDcaPopulator.php index 6e5555ae..ceee5c47 100644 --- a/src/Contao/Dca/Populator/ExtendedLegacyDcaPopulator.php +++ b/src/Contao/Dca/Populator/ExtendedLegacyDcaPopulator.php @@ -126,7 +126,7 @@ public function populateController(EnvironmentInterface $environment) */ public function populate(EnvironmentInterface $environment) { - if (!$this->scopeDeterminator->currentScopeIsBackend()) { + if (!$this->getScopeDeterminator()->currentScopeIsBackend()) { return; } diff --git a/src/Contao/Event/Subscriber.php b/src/Contao/Event/Subscriber.php index 2fcfb1cc..3717463f 100644 --- a/src/Contao/Event/Subscriber.php +++ b/src/Contao/Event/Subscriber.php @@ -36,6 +36,7 @@ use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\GetPropertyOptionsEvent; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\ResolveWidgetErrorMessageEvent; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\ViewHelpers; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\Properties\PropertyInterface; use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; use ContaoCommunityAlliance\DcGeneral\DcGeneralEvents; @@ -173,7 +174,7 @@ protected static function getOptions( EnvironmentInterface $environment, ModelInterface $model, PropertyInterface $property - ): array { + ): ?array { if (null === $dispatcher = $environment->getEventDispatcher()) { return $property->getOptions(); } @@ -207,7 +208,7 @@ private static function decodeValue( ->setProperty($property) ->setValue($value); - $environment->getEventDispatcher()->dispatch($event, \sprintf('%s', $event::NAME)); + $environment->getEventDispatcher()?->dispatch($event, \sprintf('%s', $event::NAME)); return $event->getValue(); } @@ -219,13 +220,13 @@ private static function decodeValue( * @param string $dateFormat The date format to use. * @param int $timeStamp The timestamp. * - * @return string + * @return ?string */ private static function parseDateTime( EventDispatcherInterface $dispatcher, string $dateFormat, int $timeStamp - ): string { + ): ?string { $dateEvent = new ParseDateEvent($timeStamp, $dateFormat); $dispatcher->dispatch($dateEvent, ContaoEvents::DATE_PARSE); @@ -270,7 +271,9 @@ public function renderReadablePropertyValue(RenderReadablePropertyValueEvent $ev case (\is_int($value)): self::renderTimestampReadable($event, $extra, $value); self::renderDateTimePropertyIsTstamp($event, $property, $value); - self::renderSimpleCheckbox($event, $property, $extra, $value); + // No break here. + case (\is_bool($value)): + self::renderSimpleCheckbox($event, $property, $extra, (int) $value); break; } @@ -294,7 +297,7 @@ public function renderReadablePropertyValue(RenderReadablePropertyValueEvent $ev * * @return void * - * @psalm-suppress UndefinedClass + * @psalm-suppress UndefinedClass - The class is only available when a twig bundle is installed. */ public function initTwig(\ContaoTwigInitializeEvent $event): void { @@ -334,21 +337,29 @@ public function initializePanels(ActionEvent $event): void $environment = $event->getEnvironment(); $definition = $environment->getDataDefinition(); $view = $environment->getView(); + if (!$definition instanceof ContainerInterface) { + return; + } if ( !$view instanceof BaseView - || !$view->getPanel() || !$definition->hasDefinition(Contao2BackendViewDefinitionInterface::NAME) ) { return; } + $panel = $view->getPanel(); + if (null === $panel) { + return; + } /** @var Contao2BackendViewDefinitionInterface $backendDefinition */ $backendDefinition = $definition->getDefinition(Contao2BackendViewDefinitionInterface::NAME); $listingConfig = $backendDefinition->getListingConfig(); - $dataConfig = $environment->getBaseConfigRegistry()->getBaseConfig(); - $panel = $view->getPanel(); + $dataConfig = $environment->getBaseConfigRegistry()?->getBaseConfig(); + if (null === $dataConfig) { + return; + } ViewHelpers::initializeSorting($panel, $dataConfig, $listingConfig); } @@ -449,6 +460,9 @@ private static function renderTimestampReadable( } $dispatcher = $event->getEnvironment()->getEventDispatcher(); + if (null === $dispatcher) { + return; + } $event->setRendered( self::parseDateTime($dispatcher, self::getConfig()->get($extra['rgxp'] . 'Format'), $value) @@ -474,6 +488,9 @@ private static function renderDateTimePropertyIsTstamp( } $dispatcher = $event->getEnvironment()->getEventDispatcher(); + if (null === $dispatcher) { + return; + } // Date and time format. $event->setRendered(self::parseDateTime($dispatcher, self::getConfig()->get('timeFormat'), $value)); @@ -502,9 +519,13 @@ private static function renderSimpleCheckbox( return; } - $map = [false => 'no', true => 'yes']; + $map = [0 => 'no', 1 => 'yes']; + $translator = $event->getEnvironment()->getTranslator(); + if (null === $translator) { + return; + } - $event->setRendered($event->getEnvironment()->getTranslator()->translate('MSC.' . $map[(bool) $value])); + $event->setRendered($translator->translate('MSC.' . $map[$value])); } /** @@ -518,9 +539,12 @@ private static function renderSimpleCheckbox( private static function renderDateTimeValueInstance(RenderReadablePropertyValueEvent $event, DateTime $value): void { $dispatcher = $event->getEnvironment()->getEventDispatcher(); + if (null === $dispatcher) { + return; + } $event->setRendered( - self::parseDateTime($dispatcher, self::getConfig()->get('datimFormat'), $value->getTimestamp()) + self::parseDateTime($dispatcher, self::getConfig()->get('datimFormat') ?? '', $value->getTimestamp()) ); } @@ -596,6 +620,11 @@ private static function renderOptionValueReadable( PropertyInterface $property, mixed $value ): void { + // Can not be an array key. + if ((null !== $value) && !is_scalar($value)) { + return; + } + if (!($options = $property->getOptions())) { $options = self::getOptions($event->getEnvironment(), $event->getModel(), $event->getProperty()); if ($options) { diff --git a/src/Contao/Factory/SessionStorageFactory.php b/src/Contao/Factory/SessionStorageFactory.php index 48e788ba..0247535a 100644 --- a/src/Contao/Factory/SessionStorageFactory.php +++ b/src/Contao/Factory/SessionStorageFactory.php @@ -21,6 +21,7 @@ use ContaoCommunityAlliance\DcGeneral\Contao\SessionStorage; use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\HttpFoundation\Session\SessionInterface; /** * This factory create a new session storage @@ -51,9 +52,12 @@ public function __construct(ContainerInterface $container) */ public function createService() { - return new SessionStorage( - $this->container->get('session'), - $this->container->getParameter('cca.dc-general.session.database_keys') - ); + $session = $this->container->get('session'); + assert($session instanceof SessionInterface); + $keys = $this->container->getParameter('cca.dc-general.session.database_keys'); + assert(is_array($keys)); + /** @var list $keys */ + + return new SessionStorage($session, $keys); } } diff --git a/src/Contao/Picker/AbstractAwarePickerProvider.php b/src/Contao/Picker/AbstractAwarePickerProvider.php index 316bbbac..a5399b5f 100644 --- a/src/Contao/Picker/AbstractAwarePickerProvider.php +++ b/src/Contao/Picker/AbstractAwarePickerProvider.php @@ -50,9 +50,9 @@ abstract class AbstractAwarePickerProvider implements PickerProviderInterface /** * The token storage. * - * @var TokenStorageInterface + * @var ?TokenStorageInterface */ - private $tokenStorage; + private $tokenStorage = null; /** * The translator. diff --git a/src/Contao/Picker/PagePickerProvider.php b/src/Contao/Picker/PagePickerProvider.php index bdec67a6..52bbfe42 100644 --- a/src/Contao/Picker/PagePickerProvider.php +++ b/src/Contao/Picker/PagePickerProvider.php @@ -86,7 +86,7 @@ public function getDcaAttributes(PickerConfig $config) } if ($value) { - $intval = function ($val) { + $intval = function (mixed $val): int { return (int) $val; }; diff --git a/src/Contao/RequestScopeDeterminatorAwareTrait.php b/src/Contao/RequestScopeDeterminatorAwareTrait.php index e0da90fe..5d8f2e1f 100644 --- a/src/Contao/RequestScopeDeterminatorAwareTrait.php +++ b/src/Contao/RequestScopeDeterminatorAwareTrait.php @@ -28,9 +28,9 @@ trait RequestScopeDeterminatorAwareTrait /** * The request mode determinator. * - * @var RequestScopeDeterminator + * @var null|RequestScopeDeterminator */ - private $scopeDeterminator; + private ?RequestScopeDeterminator $scopeDeterminator = null; /** * ClipboardController constructor. @@ -39,8 +39,17 @@ trait RequestScopeDeterminatorAwareTrait * * @return void */ - public function setScopeDeterminator(RequestScopeDeterminator $scopeDeterminator) + public function setScopeDeterminator(RequestScopeDeterminator $scopeDeterminator): void { $this->scopeDeterminator = $scopeDeterminator; } + + private function getScopeDeterminator(): RequestScopeDeterminator + { + if (null === $this->scopeDeterminator) { + throw new \RuntimeException('scopeDeterminator has not been set.'); + } + + return $this->scopeDeterminator; + } } diff --git a/src/Contao/SessionStorage.php b/src/Contao/SessionStorage.php index af5db78c..d5354c70 100644 --- a/src/Contao/SessionStorage.php +++ b/src/Contao/SessionStorage.php @@ -222,8 +222,8 @@ private function persist() private function filterAttributes($determineDatabase = false) { $databaseAttributes = \array_merge( - isset($this->databaseKeys['common']) ? $this->databaseKeys['common'] : [], - isset($this->databaseKeys[$this->getScope()]) ? $this->databaseKeys[$this->getScope()] : [] + $this->databaseKeys['common'] ?? [], + $this->databaseKeys[$this->getScope()] ?? [] ); if ($determineDatabase) { @@ -238,7 +238,7 @@ private function filterAttributes($determineDatabase = false) * * @return string|null */ - private function getScope() + private function getScope(): ?string { if (!$this->scope) { // @codingStandardsIgnoreStart diff --git a/src/Contao/Subscriber/FormatModelLabelSubscriber.php b/src/Contao/Subscriber/FormatModelLabelSubscriber.php index 1b1f1e5f..449bf3a1 100644 --- a/src/Contao/Subscriber/FormatModelLabelSubscriber.php +++ b/src/Contao/Subscriber/FormatModelLabelSubscriber.php @@ -49,7 +49,7 @@ class FormatModelLabelSubscriber */ public function handleFormatModelLabel(FormatModelLabelEvent $event) { - if (!$this->scopeDeterminator->currentScopeIsBackend()) { + if (!$this->getScopeDeterminator()->currentScopeIsBackend()) { return; } diff --git a/src/Contao/View/Contao2BackendView/ActionHandler/AbstractListShowAllHandler.php b/src/Contao/View/Contao2BackendView/ActionHandler/AbstractListShowAllHandler.php index 12f30709..5821695d 100644 --- a/src/Contao/View/Contao2BackendView/ActionHandler/AbstractListShowAllHandler.php +++ b/src/Contao/View/Contao2BackendView/ActionHandler/AbstractListShowAllHandler.php @@ -165,7 +165,7 @@ public function __construct( */ public function handleEvent(ActionEvent $event) { - if (!$this->scopeDeterminator->currentScopeIsBackend()) { + if (!$this->getScopeDeterminator()->currentScopeIsBackend()) { return; } diff --git a/src/Contao/View/Contao2BackendView/ActionHandler/CopyHandler.php b/src/Contao/View/Contao2BackendView/ActionHandler/CopyHandler.php index e0b54933..6cec1b9d 100644 --- a/src/Contao/View/Contao2BackendView/ActionHandler/CopyHandler.php +++ b/src/Contao/View/Contao2BackendView/ActionHandler/CopyHandler.php @@ -84,7 +84,7 @@ public function __construct(RequestScopeDeterminator $scopeDeterminator, CsrfUrl */ public function handleEvent(ActionEvent $event) { - if (!$this->scopeDeterminator->currentScopeIsBackend()) { + if (!$this->getScopeDeterminator()->currentScopeIsBackend()) { return; } diff --git a/src/Contao/View/Contao2BackendView/ActionHandler/CreateHandler.php b/src/Contao/View/Contao2BackendView/ActionHandler/CreateHandler.php index a7317ca2..f5c07f22 100644 --- a/src/Contao/View/Contao2BackendView/ActionHandler/CreateHandler.php +++ b/src/Contao/View/Contao2BackendView/ActionHandler/CreateHandler.php @@ -72,7 +72,7 @@ public function __construct(RequestScopeDeterminator $scopeDeterminator, Default */ public function handleEvent(ActionEvent $event) { - if (!$this->scopeDeterminator->currentScopeIsBackend()) { + if (!$this->getScopeDeterminator()->currentScopeIsBackend()) { return; } diff --git a/src/Contao/View/Contao2BackendView/ActionHandler/DeleteHandler.php b/src/Contao/View/Contao2BackendView/ActionHandler/DeleteHandler.php index 26fca809..f2e74dee 100644 --- a/src/Contao/View/Contao2BackendView/ActionHandler/DeleteHandler.php +++ b/src/Contao/View/Contao2BackendView/ActionHandler/DeleteHandler.php @@ -75,7 +75,7 @@ public function __construct(RequestScopeDeterminator $scopeDeterminator) */ public function handleEvent(ActionEvent $event) { - if (!$this->scopeDeterminator->currentScopeIsBackend()) { + if (!$this->getScopeDeterminator()->currentScopeIsBackend()) { return; } diff --git a/src/Contao/View/Contao2BackendView/ActionHandler/EditHandler.php b/src/Contao/View/Contao2BackendView/ActionHandler/EditHandler.php index 3e9de8e4..7751ae73 100644 --- a/src/Contao/View/Contao2BackendView/ActionHandler/EditHandler.php +++ b/src/Contao/View/Contao2BackendView/ActionHandler/EditHandler.php @@ -74,7 +74,7 @@ public function __construct(RequestScopeDeterminator $scopeDeterminator, Default */ public function handleEvent(ActionEvent $event) { - if (!$this->scopeDeterminator->currentScopeIsBackend()) { + if (!$this->getScopeDeterminator()->currentScopeIsBackend()) { return; } diff --git a/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/EditAllHandler.php b/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/EditAllHandler.php index b7c5f125..995c09c3 100644 --- a/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/EditAllHandler.php +++ b/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/EditAllHandler.php @@ -63,7 +63,7 @@ public function __construct(RequestScopeDeterminator $scopeDeterminator) public function handleEvent(ActionEvent $event) { if ( - !$this->scopeDeterminator->currentScopeIsBackend() + !$this->getScopeDeterminator()->currentScopeIsBackend() || ('editAll' !== $event->getAction()->getName()) ) { return; diff --git a/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/OverrideAllHandler.php b/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/OverrideAllHandler.php index 87014428..c438ae8d 100644 --- a/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/OverrideAllHandler.php +++ b/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/OverrideAllHandler.php @@ -59,7 +59,7 @@ public function __construct(RequestScopeDeterminator $scopeDeterminator) public function handleEvent(ActionEvent $event) { if ( - !$this->scopeDeterminator->currentScopeIsBackend() + !$this->getScopeDeterminator()->currentScopeIsBackend() || ('overrideAll' !== $event->getAction()->getName()) ) { return; diff --git a/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/PasteAllHandler.php b/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/PasteAllHandler.php index 493f936d..e8f8d568 100644 --- a/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/PasteAllHandler.php +++ b/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/PasteAllHandler.php @@ -73,7 +73,7 @@ public function __construct(RequestScopeDeterminator $scopeDeterminator) */ public function handleEvent(ActionEvent $event) { - if (!$this->scopeDeterminator->currentScopeIsBackend() || ('pasteAll' !== $event->getAction()->getName())) { + if (!$this->getScopeDeterminator()->currentScopeIsBackend() || ('pasteAll' !== $event->getAction()->getName())) { return; } diff --git a/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/SelectModelAllHandler.php b/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/SelectModelAllHandler.php index 256a39d7..56b22af9 100644 --- a/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/SelectModelAllHandler.php +++ b/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/SelectModelAllHandler.php @@ -54,7 +54,7 @@ public function __construct(RequestScopeDeterminator $scopeDeterminator) public function handleEvent(ActionEvent $event) { if ( - !$this->scopeDeterminator->currentScopeIsBackend() + !$this->getScopeDeterminator()->currentScopeIsBackend() || ('selectModelAll' !== $event->getAction()->getName()) ) { return; diff --git a/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/SelectPropertyAllHandler.php b/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/SelectPropertyAllHandler.php index 2f9a05d9..acbdfcc6 100644 --- a/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/SelectPropertyAllHandler.php +++ b/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/SelectPropertyAllHandler.php @@ -61,7 +61,7 @@ class SelectPropertyAllHandler extends AbstractListShowAllHandler public function handleEvent(ActionEvent $event) { if ( - !$this->scopeDeterminator->currentScopeIsBackend() + !$this->getScopeDeterminator()->currentScopeIsBackend() || ('selectPropertyAll' !== $event->getAction()->getName()) ) { return null; diff --git a/src/Contao/View/Contao2BackendView/ActionHandler/PasteHandler.php b/src/Contao/View/Contao2BackendView/ActionHandler/PasteHandler.php index 78162aa1..a94d2c23 100644 --- a/src/Contao/View/Contao2BackendView/ActionHandler/PasteHandler.php +++ b/src/Contao/View/Contao2BackendView/ActionHandler/PasteHandler.php @@ -60,7 +60,7 @@ public function __construct(RequestScopeDeterminator $scopeDeterminator) */ public function handleEvent(ActionEvent $event) { - if (!$this->scopeDeterminator->currentScopeIsBackend()) { + if (!$this->getScopeDeterminator()->currentScopeIsBackend()) { return; } diff --git a/src/Contao/View/Contao2BackendView/ActionHandler/SelectHandler.php b/src/Contao/View/Contao2BackendView/ActionHandler/SelectHandler.php index ce4ca628..27de31fa 100644 --- a/src/Contao/View/Contao2BackendView/ActionHandler/SelectHandler.php +++ b/src/Contao/View/Contao2BackendView/ActionHandler/SelectHandler.php @@ -74,7 +74,7 @@ public function __construct(RequestScopeDeterminator $scopeDeterminator) */ public function handleEvent(ActionEvent $event) { - if (!$this->scopeDeterminator->currentScopeIsBackend()) { + if (!$this->getScopeDeterminator()->currentScopeIsBackend()) { return; } diff --git a/src/Contao/View/Contao2BackendView/ActionHandler/ToggleHandler.php b/src/Contao/View/Contao2BackendView/ActionHandler/ToggleHandler.php index 508b8925..1dda36f1 100644 --- a/src/Contao/View/Contao2BackendView/ActionHandler/ToggleHandler.php +++ b/src/Contao/View/Contao2BackendView/ActionHandler/ToggleHandler.php @@ -67,7 +67,7 @@ public function __construct(RequestScopeDeterminator $scopeDeterminator) */ public function handleEvent(ActionEvent $event) { - if (!$this->scopeDeterminator->currentScopeIsBackend()) { + if (!$this->getScopeDeterminator()->currentScopeIsBackend()) { return; } diff --git a/src/Contao/View/Contao2BackendView/BaseView.php b/src/Contao/View/Contao2BackendView/BaseView.php index e48c9c50..332d4bb0 100644 --- a/src/Contao/View/Contao2BackendView/BaseView.php +++ b/src/Contao/View/Contao2BackendView/BaseView.php @@ -130,7 +130,7 @@ public function handleAction(ActionEvent $event) $name = $action->getName(); if ('show' === $name) { - $handler = new ShowHandler($this->scopeDeterminator); + $handler = new ShowHandler($this->getScopeDeterminator()); $handler->handleEvent($event); return; diff --git a/src/Contao/View/Contao2BackendView/ButtonRenderer.php b/src/Contao/View/Contao2BackendView/ButtonRenderer.php index 7387743e..de0de205 100644 --- a/src/Contao/View/Contao2BackendView/ButtonRenderer.php +++ b/src/Contao/View/Contao2BackendView/ButtonRenderer.php @@ -649,6 +649,9 @@ private function getCommandLabel(CommandInterface $command) $label = $command->getName(); } + if (\is_array($label)) { + return $this->translate($label[0]); + } $label = $this->translate($label); return \is_array($label) ? $label[0] : $label; diff --git a/src/Contao/View/Contao2BackendView/EventListener/BackButtonListener.php b/src/Contao/View/Contao2BackendView/EventListener/BackButtonListener.php index 42c83174..81c1fe37 100644 --- a/src/Contao/View/Contao2BackendView/EventListener/BackButtonListener.php +++ b/src/Contao/View/Contao2BackendView/EventListener/BackButtonListener.php @@ -42,7 +42,7 @@ class BackButtonListener */ public function handle(GetGlobalButtonEvent $event) { - if (!$this->scopeDeterminator->currentScopeIsBackend()) { + if (!$this->getScopeDeterminator()->currentScopeIsBackend()) { return; } diff --git a/src/Contao/View/Contao2BackendView/EventListener/CreateModelButtonListener.php b/src/Contao/View/Contao2BackendView/EventListener/CreateModelButtonListener.php index 8af94452..bba77a07 100644 --- a/src/Contao/View/Contao2BackendView/EventListener/CreateModelButtonListener.php +++ b/src/Contao/View/Contao2BackendView/EventListener/CreateModelButtonListener.php @@ -43,7 +43,7 @@ class CreateModelButtonListener */ public function handle(GetGlobalButtonEvent $event) { - if (!$this->scopeDeterminator->currentScopeIsBackend()) { + if (!$this->getScopeDeterminator()->currentScopeIsBackend()) { return; } diff --git a/src/Contao/View/Contao2BackendView/Subscriber/GetGroupHeaderSubscriber.php b/src/Contao/View/Contao2BackendView/Subscriber/GetGroupHeaderSubscriber.php index 2ecf4d88..3528fac1 100644 --- a/src/Contao/View/Contao2BackendView/Subscriber/GetGroupHeaderSubscriber.php +++ b/src/Contao/View/Contao2BackendView/Subscriber/GetGroupHeaderSubscriber.php @@ -81,7 +81,7 @@ public function __construct(EventDispatcherInterface $dispatcher, TranslatorInte */ public function handle(GetGroupHeaderEvent $event) { - if ((null !== $event->getValue()) || !$this->scopeDeterminator->currentScopeIsBackend()) { + if ((null !== $event->getValue()) || !$this->getScopeDeterminator()->currentScopeIsBackend()) { return; } diff --git a/src/Contao/View/Contao2BackendView/Subscriber/MultipleHandlerSubscriber.php b/src/Contao/View/Contao2BackendView/Subscriber/MultipleHandlerSubscriber.php index 500573b9..8e0164a4 100644 --- a/src/Contao/View/Contao2BackendView/Subscriber/MultipleHandlerSubscriber.php +++ b/src/Contao/View/Contao2BackendView/Subscriber/MultipleHandlerSubscriber.php @@ -91,7 +91,7 @@ public static function getSubscribedEvents() */ public function prepareGlobalAllButton(ActionEvent $event) { - if (!$this->scopeDeterminator->currentScopeIsBackend() || ('showAll' !== $event->getAction()->getName())) { + if (!$this->getScopeDeterminator()->currentScopeIsBackend() || ('showAll' !== $event->getAction()->getName())) { return; } @@ -118,7 +118,7 @@ public function deactivateGlobalButton(ActionEvent $event) { $allowedAction = ['selectModelAll', 'selectPropertyAll', 'editAll', 'overrideAll']; if ( - !$this->scopeDeterminator->currentScopeIsBackend() + !$this->getScopeDeterminator()->currentScopeIsBackend() || !\in_array($event->getAction()->getName(), $allowedAction) ) { return; @@ -154,7 +154,7 @@ public function handleOriginalOptions(GetOptionsEvent $event) $environment = $event->getEnvironment(); if ( - !$this->scopeDeterminator->currentScopeIsBackend() + !$this->getScopeDeterminator()->currentScopeIsBackend() || ('select' !== $environment->getInputProvider()->getParameter('act')) || ('edit' !== $environment->getInputProvider()->getParameter('select')) ) { @@ -203,7 +203,7 @@ public function handleOriginalWidget(BuildWidgetEvent $event) $environment = $event->getEnvironment(); if ( - !$this->scopeDeterminator->currentScopeIsBackend() + !$this->getScopeDeterminator()->currentScopeIsBackend() || ('select' !== $environment->getInputProvider()->getParameter('act')) || ('edit' !== $environment->getInputProvider()->getParameter('select')) ) { diff --git a/src/DataDefinition/ContainerInterface.php b/src/DataDefinition/ContainerInterface.php index ed126be9..8bd5c850 100644 --- a/src/DataDefinition/ContainerInterface.php +++ b/src/DataDefinition/ContainerInterface.php @@ -64,7 +64,7 @@ public function clearDefinitions(); /** * Set the definitions of this container. * - * @param array|DefinitionInterface[] $definitions The definitons. + * @param array $definitions The definitons. * * @return ContainerInterface */ @@ -73,7 +73,7 @@ public function setDefinitions(array $definitions); /** * Add multiple definitions to this container. * - * @param array|DefinitionInterface[] $definitions The definitons. + * @param array $definitions The definitons. * * @return ContainerInterface */ diff --git a/src/DataDefinition/Palette/Legend.php b/src/DataDefinition/Palette/Legend.php index ec554e25..2d96ce05 100644 --- a/src/DataDefinition/Palette/Legend.php +++ b/src/DataDefinition/Palette/Legend.php @@ -22,7 +22,7 @@ namespace ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette; use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; -use ContaoCommunityAlliance\DcGeneral\Data\PropertyValueBag; +use ContaoCommunityAlliance\DcGeneral\Data\PropertyValueBagInterface; use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralInvalidArgumentException; use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralRuntimeException; @@ -205,7 +205,7 @@ public function removeProperty(PropertyInterface $property) /** * {@inheritdoc} */ - public function getProperties(ModelInterface $model = null, PropertyValueBag $input = null) + public function getProperties(?ModelInterface $model = null, ?PropertyValueBagInterface $input = null) { if ($model || $input) { $selectedProperties = []; diff --git a/src/DataDefinition/Palette/LegendInterface.php b/src/DataDefinition/Palette/LegendInterface.php index 701bf882..2a7a38cc 100644 --- a/src/DataDefinition/Palette/LegendInterface.php +++ b/src/DataDefinition/Palette/LegendInterface.php @@ -23,6 +23,7 @@ use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; use ContaoCommunityAlliance\DcGeneral\Data\PropertyValueBag; +use ContaoCommunityAlliance\DcGeneral\Data\PropertyValueBagInterface; use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralRuntimeException; /** @@ -127,16 +128,16 @@ public function removeProperty(PropertyInterface $property); /** * Get all properties in this legend. * - * @param ModelInterface|null $model If given, subpalettes will be evaluated depending on the model. - * If no model is given, all properties will be returned, including subpalette - * properties. - * @param PropertyValueBag $input If given, subpalettes will be evaluated depending on the input data. - * If no model and no input data is given, all properties will be returned, - * including subpalette properties. + * @param ModelInterface|null $model If given, subpalettes will be evaluated depending on the model. + * If no model is given, all properties will be returned, including + * subpalette properties. + * @param PropertyValueBagInterface|null $input If given, subpalettes will be evaluated depending on the input data. + * If no model and no input data is given, all properties will be + * returned, including subpalette properties. * * @return PropertyInterface[] */ - public function getProperties(ModelInterface $model = null, PropertyValueBag $input = null); + public function getProperties(?ModelInterface $model = null, ?PropertyValueBagInterface $input = null); /** * Determine if a property with the name exists in this legend. diff --git a/src/DataDefinition/Palette/PaletteCollection.php b/src/DataDefinition/Palette/PaletteCollection.php index 1ed1ce4e..9ee1b080 100644 --- a/src/DataDefinition/Palette/PaletteCollection.php +++ b/src/DataDefinition/Palette/PaletteCollection.php @@ -22,7 +22,7 @@ namespace ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette; use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; -use ContaoCommunityAlliance\DcGeneral\Data\PropertyValueBag; +use ContaoCommunityAlliance\DcGeneral\Data\PropertyValueBagInterface; use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralInvalidArgumentException; /** @@ -108,7 +108,7 @@ public function hasPalette(PaletteInterface $palette) * @throws DcGeneralInvalidArgumentException Is thrown if there is no palettes found. * @throws DcGeneralInvalidArgumentException Is thrown if there is no palette found or more than one palette. */ - public function findPalette(ModelInterface $model = null, PropertyValueBag $input = null) + public function findPalette(ModelInterface $model = null, PropertyValueBagInterface $input = null) { $matches = []; diff --git a/src/DataDefinition/Palette/PaletteCollectionInterface.php b/src/DataDefinition/Palette/PaletteCollectionInterface.php index fc8e6554..cbe4cea7 100644 --- a/src/DataDefinition/Palette/PaletteCollectionInterface.php +++ b/src/DataDefinition/Palette/PaletteCollectionInterface.php @@ -23,6 +23,7 @@ use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; use ContaoCommunityAlliance\DcGeneral\Data\PropertyValueBag; +use ContaoCommunityAlliance\DcGeneral\Data\PropertyValueBagInterface; /** * Contains multiple palettes, organised by its name. @@ -91,12 +92,12 @@ public function hasPalette(PaletteInterface $palette); /** * Find the palette matching model and input parameters. * - * @param ModelInterface|null $model If given, selectors will be evaluated depending on the model. - * @param PropertyValueBag $input If given, selectors will be evaluated depending on the input data. + * @param ModelInterface|null $model If given, selectors will be evaluated depending on the model. + * @param PropertyValueBagInterface|null $input If given, selectors will be evaluated depending on the input data. * * @return PaletteInterface */ - public function findPalette(ModelInterface $model = null, PropertyValueBag $input = null); + public function findPalette(ModelInterface $model = null, PropertyValueBagInterface $input = null); /** * Check if a palette for the given name exists in this collection. diff --git a/src/EnvironmentPopulator/AbstractEventDrivenEnvironmentPopulator.php b/src/EnvironmentPopulator/AbstractEventDrivenEnvironmentPopulator.php index 680dec59..cd169618 100644 --- a/src/EnvironmentPopulator/AbstractEventDrivenEnvironmentPopulator.php +++ b/src/EnvironmentPopulator/AbstractEventDrivenEnvironmentPopulator.php @@ -33,8 +33,10 @@ abstract class AbstractEventDrivenEnvironmentPopulator implements EnvironmentPop /** * Priority of the listener. * Just here vor sanity, must be overwritten by implementation. + * + * @var int */ - public const PRIORITY = null; + public const PRIORITY = 0; /** * Creates an instance of itself and processes the event. diff --git a/src/InputProviderInterface.php b/src/InputProviderInterface.php index 198ecd1e..6c647336 100644 --- a/src/InputProviderInterface.php +++ b/src/InputProviderInterface.php @@ -33,94 +33,94 @@ interface InputProviderInterface * * In plain HTTP, this will be a $_GET parameter, for other implementations consult the API. * - * @param string $strKey The name of the parameter to be retrieved. - * @param bool $blnRaw Boolean flag to determine if the content shall be returned RAW or rather be stripped of - * potential malicious content. + * @param string $key The name of the parameter to be retrieved. + * @param bool $raw Boolean flag to determine if the content shall be returned RAW or rather be stripped of + * potential malicious content. * * @return mixed */ - public function getParameter($strKey, $blnRaw = false); + public function getParameter($key, $raw = false); /** * Save/change a request parameter. * * In plain HTTP, this will be a $_GET parameter, for other implementations consult the API. * - * @param string $strKey The name of the parameter to be stored. - * @param mixed $varValue The value to be stored. + * @param string $key The name of the parameter to be stored. + * @param mixed $value The value to be stored. * * @return InputProviderInterface */ - public function setParameter($strKey, $varValue); + public function setParameter($key, $value); /** * Unset a request parameter. * * In plain HTTP, this will be a $_GET parameter, for other implementations consult the API. * - * @param string $strKey The name of the parameter to be removed. + * @param string $key The name of the parameter to be removed. * * @return InputProviderInterface */ - public function unsetParameter($strKey); + public function unsetParameter($key); /** * Determines if a request parameter is defined. * * In plain HTTP, this will be a $_GET parameter, for other implementations consult the API. * - * @param string $strKey The name of the parameter to be checked. + * @param string $key The name of the parameter to be checked. * * @return bool */ - public function hasParameter($strKey); + public function hasParameter($key); /** * Retrieve a request value. * * In plain HTTP, this will be a $_POST value, for other implementations consult the API. * - * @param string $strKey The name of the value to be retrieved. - * @param bool $blnRaw Boolean flag to determine if the content shall be returned RAW or rather be stripped of - * potential malicious content. + * @param string $key The name of the value to be retrieved. + * @param bool $raw Boolean flag to determine if the content shall be returned RAW or rather be stripped of + * potential malicious content. * * @return mixed */ - public function getValue($strKey, $blnRaw = false); + public function getValue($key, $raw = false); /** * Save/change a request value. * * In plain HTTP, this will be a $_POST value, for other implementations consult the API. * - * @param string $strKey The name of the value to be stored. - * @param mixed $varValue The value to be stored. + * @param string $key The name of the value to be stored. + * @param mixed $value The value to be stored. * * @return InputProviderInterface */ - public function setValue($strKey, $varValue); + public function setValue($key, $value); /** * Unset a request value. * * In plain HTTP, this will be a $_POST value, for other implementations consult the API. * - * @param string $strKey The name of the value to be removed. + * @param string $key The name of the value to be removed. * * @return InputProviderInterface */ - public function unsetValue($strKey); + public function unsetValue($key); /** * Determines if a request value is defined. * * In plain HTTP, this will be a $_POST value, for other implementations consult the API. * - * @param string $strKey The name of the value to be checked. + * @param string $key The name of the value to be checked. * * @return bool */ - public function hasValue($strKey); + public function hasValue($key); /** * Retrieve the current request url. diff --git a/src/View/ActionHandler/AbstractHandler.php b/src/View/ActionHandler/AbstractHandler.php index 936c6954..77f27596 100644 --- a/src/View/ActionHandler/AbstractHandler.php +++ b/src/View/ActionHandler/AbstractHandler.php @@ -25,10 +25,12 @@ use ContaoCommunityAlliance\DcGeneral\Action; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Exception\EditOnlyModeException; use ContaoCommunityAlliance\DcGeneral\Data\ModelIdInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\DcGeneralEvents; use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; use ContaoCommunityAlliance\DcGeneral\Event\ActionEvent; use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralRuntimeException; +use LogicException; /** * Abstract base class for handling dc-general action events. @@ -40,9 +42,9 @@ abstract class AbstractHandler /** * The event. * - * @var ActionEvent + * @var ActionEvent|null */ - private $event; + private ?ActionEvent $event = null; /** * Method to buffer the event and then process it. @@ -65,6 +67,9 @@ public function handleEvent(ActionEvent $event) */ protected function getEvent() { + if (null === $this->event) { + throw new LogicException('No event set.'); + } return $this->event; } @@ -89,7 +94,7 @@ protected function getEnvironment() */ protected function guardValidEnvironment(ModelIdInterface $modelId) { - if ($this->getEnvironment()->getDataDefinition()->getName() !== $modelId->getDataProviderName()) { + if ($this->getDataDefinition()->getName() !== $modelId->getDataProviderName()) { throw new DcGeneralRuntimeException( \sprintf( 'Not able to perform action. Environment is not prepared for model "%s"', @@ -110,7 +115,7 @@ protected function guardValidEnvironment(ModelIdInterface $modelId) */ protected function guardNotEditOnly(ModelIdInterface $modelId) { - if ($this->getEnvironment()->getDataDefinition()->getBasicDefinition()->isEditOnlyMode()) { + if ($this->getDataDefinition()->getBasicDefinition()->isEditOnlyMode()) { throw new EditOnlyModeException($modelId->getDataProviderName()); } } @@ -124,7 +129,7 @@ protected function guardNotEditOnly(ModelIdInterface $modelId) */ protected function isEditOnlyResponse() { - if ($this->getEnvironment()->getDataDefinition()->getBasicDefinition()->isEditOnlyMode()) { + if ($this->getDataDefinition()->getBasicDefinition()->isEditOnlyMode()) { $this->callAction('edit'); return true; @@ -143,10 +148,15 @@ protected function isEditOnlyResponse() */ protected function callAction($actionName, $arguments = []) { + $environment = $this->getEnvironment(); // Keep the event as we might get called recursively. - $keepEvent = $this->event; - $event = new ActionEvent($this->getEnvironment(), new Action($actionName, $arguments)); - $this->getEnvironment()->getEventDispatcher()->dispatch($event, DcGeneralEvents::ACTION); + $keepEvent = $this->event; + $event = new ActionEvent($environment, new Action($actionName, $arguments)); + $dispatcher = $environment->getEventDispatcher(); + if (null === $dispatcher) { + throw new LogicException('No event dispatcher found in environment.'); + } + $dispatcher->dispatch($event, DcGeneralEvents::ACTION); // Restore the event as we might get called recursively. $this->event = $keepEvent; @@ -159,4 +169,13 @@ protected function callAction($actionName, $arguments = []) * @return void */ abstract public function process(); + + private function getDataDefinition(): ContainerInterface + { + if (null === $definition = $this->getEnvironment()->getDataDefinition()) { + throw new LogicException('No data definition found in environment.'); + } + + return $definition; + } } diff --git a/src/View/ActionHandler/AbstractPropertyOverrideEditAllHandler.php b/src/View/ActionHandler/AbstractPropertyOverrideEditAllHandler.php index a7ff7194..f09946b6 100644 --- a/src/View/ActionHandler/AbstractPropertyOverrideEditAllHandler.php +++ b/src/View/ActionHandler/AbstractPropertyOverrideEditAllHandler.php @@ -36,10 +36,13 @@ use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; use ContaoCommunityAlliance\DcGeneral\Data\PropertyValueBag; use ContaoCommunityAlliance\DcGeneral\Data\PropertyValueBagInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; use ContaoCommunityAlliance\DcGeneral\Event\PostPersistModelEvent; use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralInvalidArgumentException; use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralRuntimeException; +use ContaoCommunityAlliance\DcGeneral\SessionStorageInterface; +use LogicException; /** * This class is the abstract base for override/edit all "overrideAll/editAll" commands. @@ -72,10 +75,11 @@ protected function handleSubmit(Action $action, EnvironmentInterface $environmen ) { return; } + $definition = $this->getDataDefinition($environment); - $sessionStorage->remove($environment->getDataDefinition()->getName() . '.' . $this->getMode($action)); + $sessionStorage->remove($definition->getName() . '.' . $this->getMode($action)); - $urlEvent = new GetReferrerEvent(false, $environment->getDataDefinition()->getName()); + $urlEvent = new GetReferrerEvent(false, $definition->getName()); $eventDispatcher->dispatch($urlEvent, ContaoEvents::SYSTEM_GET_REFERRER); $eventDispatcher->dispatch(new RedirectEvent($urlEvent->getReferrerUrl()), ContaoEvents::CONTROLLER_REDIRECT); @@ -531,7 +535,7 @@ protected function renderTemplate(Action $action, array $config) * @param ModelInterface $model The model. * @param EnvironmentInterface $environment The environment. * - * @return PropertyValueBag + * @return PropertyValueBagInterface * * @throws DcGeneralInvalidArgumentException If create property value bug, the construct argument isn´t right. * @@ -542,7 +546,7 @@ protected function getPropertyValueBagFromModel( ModelInterface $model, EnvironmentInterface $environment ) { - $propertiesDefinition = $environment->getDataDefinition()->getPropertiesDefinition(); + $propertiesDefinition = $this->getDataDefinition($environment)->getPropertiesDefinition(); $editInformation = System::getContainer()->get('cca.dc-general.edit-information'); $propertyValueBag = new PropertyValueBag(); @@ -587,7 +591,7 @@ protected function getCollectionFromSession(Action $action, EnvironmentInterface { $inputProvider = $environment->getInputProvider(); $sessionStorage = $environment->getSessionStorage(); - $dataDefinition = $environment->getDataDefinition(); + $dataDefinition = $this->getDataDefinition($environment); $dataProvider = $environment->getDataProvider($dataDefinition->getName()); $addEditProperties = @@ -758,7 +762,7 @@ private function getMode(Action $action) */ protected function getSession(Action $action, EnvironmentInterface $environment) { - $dataDefinition = $environment->getDataDefinition(); + $dataDefinition = $this->getDataDefinition($environment); $sessionStorage = $environment->getSessionStorage(); $session = $sessionStorage->get($dataDefinition->getName() . '.' . $this->getMode($action)); @@ -771,7 +775,7 @@ protected function getSession(Action $action, EnvironmentInterface $environment) */ protected function getPropertiesFromSession(Action $action, EnvironmentInterface $environment) { - $dataDefinition = $environment->getDataDefinition(); + $dataDefinition = $this->getDataDefinition($environment); $session = $this->getSession($action, $environment); @@ -791,4 +795,24 @@ protected function getPropertiesFromSession(Action $action, EnvironmentInterface return $properties; } + + private function getSessionStorage(EnvironmentInterface $environment): SessionStorageInterface + { + $sessionStorage = $environment->getSessionStorage(); + if (null === $sessionStorage) { + throw new LogicException('No session storage found in environment.'); + } + + return $sessionStorage; + } + + private function getDataDefinition(EnvironmentInterface $environment): ContainerInterface + { + $definition = $environment->getDataDefinition(); + if (null === $definition) { + throw new LogicException('No data definition found in environment.'); + } + + return $definition; + } } diff --git a/src/View/ActionHandler/AbstractPropertyVisibilityHandler.php b/src/View/ActionHandler/AbstractPropertyVisibilityHandler.php index 0892fa25..66713b4b 100644 --- a/src/View/ActionHandler/AbstractPropertyVisibilityHandler.php +++ b/src/View/ActionHandler/AbstractPropertyVisibilityHandler.php @@ -27,12 +27,27 @@ use ContaoCommunityAlliance\DcGeneral\DataDefinition; use ContaoCommunityAlliance\DcGeneral\DataDefinition\ConditionChainInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\ConditionInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\PalettesDefinitionInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\Properties\PropertyInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Condition\Property\PropertyTrueCondition; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\PaletteInterface; use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralInvalidArgumentException; +use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; +use ContaoCommunityAlliance\Translator\TranslatorInterface; +use LogicException; +use ReturnTypeWillChange; +use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; + +use function array_key_exists; +use function array_keys; +use function count; +use function get_class; +use function implode; +use function in_array; +use function method_exists; +use function sprintf; /** * This abstract visibility handler provide methods for the visibility of properties. @@ -50,12 +65,12 @@ abstract class AbstractPropertyVisibilityHandler * * @return void */ + #[ReturnTypeWillChange] protected function invisibleUnusedProperties(Action $action, EnvironmentInterface $environment) { - $properties = $environment->getDataDefinition()->getPropertiesDefinition(); + $properties = $this->getDataDefinition($environment)->getPropertiesDefinition(); $editProperties = $this->getPropertiesFromSession($action, $environment); - foreach ($properties->getPropertyNames() as $propertyName) { $property = $properties->getProperty($propertyName); if (isset($editProperties[$propertyName]) || !$property->getWidgetType()) { @@ -66,8 +81,10 @@ protected function invisibleUnusedProperties(Action $action, EnvironmentInterfac continue; } - $propertyClass = \get_class($property); + /** @var class-string $propertyClass */ + $propertyClass = get_class($property); + /** @var PropertyInterface $newProperty */ $newProperty = new $propertyClass($propertyName . '.dummy'); $newProperty->setLabel($property->getLabel()); $newProperty->setDescription($property->getDescription()); @@ -78,7 +95,9 @@ protected function invisibleUnusedProperties(Action $action, EnvironmentInterfac $newProperty->setWidgetType($property->getWidgetType()); $newProperty->setExplanation($property->getExplanation()); $newProperty->setExtra($property->getExtra()); - $newProperty->setOptions($property->getOptions()); + if (null !== $options = $property->getOptions()) { + $newProperty->setOptions($options); + } $properties->addProperty($newProperty); $properties->removeProperty($property); @@ -100,10 +119,10 @@ private function excludePaletteSelectorProperty( Action $action, PropertyInterface $property, EnvironmentInterface $environment - ) { - $palettesDefinition = $environment->getDataDefinition()->getPalettesDefinition(); + ): bool { + $palettesDefinition = $this->getDataDefinition($environment)->getPalettesDefinition(); - if (1 === \count($palettesDefinition->getPalettes())) { + if (1 === count($palettesDefinition->getPalettes())) { return false; } @@ -125,7 +144,7 @@ private function analyzeExcludeProperty( EnvironmentInterface $environment, PropertyInterface $property, PalettesDefinitionInterface $palettesDefinition - ) { + ): bool { $defaultPalette = $palettesDefinition->findPalette(); $defaultProperties = $defaultPalette->getProperties(); @@ -144,20 +163,21 @@ private function analyzeExcludeProperty( $event = new GetPropertyOptionsEvent($environment, $emptyModel); $event->setPropertyName($property->getName()); $event->setOptions($property->getOptions()); - $environment->getEventDispatcher()->dispatch($event, GetPropertyOptionsEvent::NAME); - if ((null === $event->getOptions()) || (0 > \count($event->getOptions()))) { + $this->getEventDispatcher($environment)->dispatch($event, GetPropertyOptionsEvent::NAME); + $options = $event->getOptions(); + if ((null === $options) || ([] === $options)) { continue; } $paletteCounter = 0; - foreach (\array_keys($event->getOptions()) as $paletteName) { + foreach (array_keys($options) as $paletteName) { $palettesDefinition->hasPaletteByName($paletteName) ? ++$paletteCounter : null; } - if ($paletteCounter !== \count($event->getOptions())) { + if ($paletteCounter !== count($options)) { continue; } - $property->setOptions($event->getOptions()); + $property->setOptions($options); $excludeProperty = true; } @@ -167,16 +187,16 @@ private function analyzeExcludeProperty( /** * Make the property invisible in all legends of each palette. * - * @param DataDefinition\Definition\Properties\PropertyInterface $property The property. - * @param EnvironmentInterface $environment The environment. + * @param PropertyInterface $property The property. + * @param EnvironmentInterface $environment The environment. * * @return void */ private function makePropertyInvisibleByPalette( - DataDefinition\Definition\Properties\PropertyInterface $property, + PropertyInterface $property, EnvironmentInterface $environment - ) { - $palettes = $environment->getDataDefinition()->getPalettesDefinition(); + ): void { + $palettes = $this->getDataDefinition($environment)->getPalettesDefinition(); foreach ($palettes->getPalettes() as $palette) { foreach ($palette->getLegends() as $legend) { @@ -204,13 +224,15 @@ private function makePropertyInvisibleByPalette( * * @return bool */ + #[ReturnTypeWillChange] protected function ensurePropertyVisibleInModel( Action $action, $propertyName, ModelInterface $model, EnvironmentInterface $environment ) { - $palettesDefinition = $environment->getDataDefinition()->getPalettesDefinition(); + $definition = $this->getDataDefinition($environment); + $palettesDefinition = $definition->getPalettesDefinition(); $propertyValues = $this->getPropertyValueBagFromModel($action, $model, $environment); $palette = $palettesDefinition->findPalette($model, $propertyValues); @@ -233,8 +255,7 @@ protected function ensurePropertyVisibleInModel( } } - $findProperty = - $environment->getDataDefinition()->getPropertiesDefinition()->getProperty($propertyName); + $findProperty = $definition->getPropertiesDefinition()->getProperty($propertyName); return $this->matchVisibilityOfPropertyInAnyPalette($action, $findProperty, $invisible, $environment); } @@ -254,15 +275,16 @@ protected function ensurePropertyVisibleInModel( private function matchVisibilityOfPropertyInAnyPalette( Action $action, PropertyInterface $property, - $invisible, + bool $invisible, EnvironmentInterface $environment - ) { - $palettesDefinition = $environment->getDataDefinition()->getPalettesDefinition(); - if (true === $invisible || 1 === \count($palettesDefinition->getPalettes())) { + ): bool { + $definition = $this->getDataDefinition($environment); + $palettesDefinition = $definition->getPalettesDefinition(); + if (true === $invisible || 1 === count($palettesDefinition->getPalettes())) { return $invisible; } - $propertiesDefinition = $environment->getDataDefinition()->getPropertiesDefinition(); + $propertiesDefinition = $definition->getPropertiesDefinition(); $defaultPalette = $palettesDefinition->findPalette(); $defaultProperties = $defaultPalette->getProperties(); $intersectModel = $this->getIntersectionModel($action, $environment); @@ -308,13 +330,14 @@ private function matchPaletteProperty( ModelInterface $intersectModel, DataDefinition\Palette\PropertyInterface $selectorProperty, EnvironmentInterface $environment - ) { - $palettesDefinition = $environment->getDataDefinition()->getPalettesDefinition(); - $propertiesDefinition = $environment->getDataDefinition()->getPropertiesDefinition(); + ): bool { + $definition = $this->getDataDefinition($environment); + $palettesDefinition = $definition->getPalettesDefinition(); + $propertiesDefinition = $definition->getPropertiesDefinition(); $invisibleProperty = false; $paletteSelectorProperty = $propertiesDefinition->getProperty($selectorProperty->getName()); - foreach (\array_keys($paletteSelectorProperty->getOptions()) as $paletteName) { + foreach (array_keys($paletteSelectorProperty->getOptions() ?? []) as $paletteName) { if (!$palettesDefinition->hasPaletteByName($paletteName)) { continue; } @@ -352,10 +375,10 @@ private function invisiblePaletteProperty( PropertyInterface $property, ModelInterface $intersectModel, DataDefinition\Palette\PropertyInterface $selectorProperty, - $paletteName, + string $paletteName, EnvironmentInterface $environment - ) { - $palettesDefinition = $environment->getDataDefinition()->getPalettesDefinition(); + ): bool { + $palettesDefinition = $this->getDataDefinition($environment)->getPalettesDefinition(); $intersectModel->setProperty($selectorProperty->getName(), $paletteName); $searchPalette = $palettesDefinition->findPalette($intersectModel); @@ -381,21 +404,22 @@ private function invisiblePaletteProperty( * Inject select parent property information, * if select an sub selector and their parent property don´t select for edit. * - * @param Action $action The action. - * @param DataDefinition\Definition\Properties\PropertyInterface $property The property. - * @param ModelInterface $model The model. - * @param EnvironmentInterface $environment The environment. + * @param Action $action The action. + * @param PropertyInterface $property The property. + * @param ModelInterface $model The model. + * @param EnvironmentInterface $environment The environment. * * @return null|string */ + #[ReturnTypeWillChange] protected function injectSelectParentPropertyInformation( Action $action, - DataDefinition\Definition\Properties\PropertyInterface $property, + PropertyInterface $property, ModelInterface $model, EnvironmentInterface $environment ) { - $translator = $environment->getTranslator(); - $palettesDefinition = $environment->getDataDefinition()->getPalettesDefinition(); + $translator = $this->getTranslator($environment); + $palettesDefinition = $this->getDataDefinition($environment)->getPalettesDefinition(); $palette = $palettesDefinition->findPalette($model); @@ -429,34 +453,35 @@ protected function injectSelectParentPropertyInformation( $labelParentProperty = !$informationProperty->getLabel() ? $propertyName : $informationProperty->getLabel(); $labelEditProperty = !$property->getLabel() ? $property->getName() : $property->getLabel(); - $information[] = \sprintf( + $information[] = sprintf( '

' . $translator->translate('MSC.select_parent_property_info') . '

', $labelParentProperty, $labelEditProperty ); } - return \implode('', $information); + return implode('', $information); } /** * Inject select sub properties information, * if select an sub selector and their properties don´t select for edit. * - * @param DataDefinition\Definition\Properties\PropertyInterface $property The property. - * @param ModelInterface $model The model. - * @param PropertyValueBagInterface $propertyValueBag The property values. - * @param EnvironmentInterface $environment The environment. + * @param PropertyInterface $property The property. + * @param ModelInterface $model The model. + * @param PropertyValueBagInterface $propertyValueBag The property values. + * @param EnvironmentInterface $environment The environment. * * @return null|string */ + #[ReturnTypeWillChange] protected function injectSelectSubPropertiesInformation( - DataDefinition\Definition\Properties\PropertyInterface $property, + PropertyInterface $property, ModelInterface $model, PropertyValueBagInterface $propertyValueBag, EnvironmentInterface $environment ) { - $translator = $environment->getTranslator(); + $translator = $this->getTranslator($environment); $properties = $this->matchInvisibleSubProperties($model, $property, $propertyValueBag, $environment); if (empty($properties)) { @@ -467,13 +492,13 @@ protected function injectSelectSubPropertiesInformation( foreach ($properties as $propertyName => $informationProperty) { $label = !$informationProperty->getLabel() ? $propertyName : $informationProperty->getLabel(); - $information[] = \sprintf( + $information[] = sprintf( '

' . $translator->translate('MSC.select_property_info') . '

', $label ); } - return \implode('', $information); + return implode('', $information); } /** @@ -491,20 +516,21 @@ private function findInvisiblePaletteSelectorProperty( ModelInterface $model, array &$invisibleProperties, EnvironmentInterface $environment - ) { - $palettesDefinition = $environment->getDataDefinition()->getPalettesDefinition(); + ): void { + $definition = $this->getDataDefinition($environment); + $palettesDefinition = $definition->getPalettesDefinition(); - if (!empty($invisibleProperties) || 1 > \count($palettesDefinition->getPalettes())) { + if (!empty($invisibleProperties) || 1 > count($palettesDefinition->getPalettes())) { return; } - $propertiesDefinition = $environment->getDataDefinition()->getPropertiesDefinition(); + $propertiesDefinition = $definition->getPropertiesDefinition(); $session = $this->getSession($action, $environment); $palette = $palettesDefinition->findPalette($model); foreach ($palette->getProperties() as $paletteProperty) { - if (!\array_key_exists($paletteProperty->getName(), $session['intersectValues'])) { + if (!array_key_exists($paletteProperty->getName(), $session['intersectValues'])) { continue; } @@ -533,15 +559,17 @@ private function matchInvisibleProperty( ConditionInterface $visibleCondition, array &$invisibleProperties, EnvironmentInterface $environment - ) { - $propertiesDefinition = $environment->getDataDefinition()->getPropertiesDefinition(); - + ): void { + $propertiesDefinition = $this->getDataDefinition($environment)->getPropertiesDefinition(); + if (!$visibleCondition instanceof ConditionChainInterface) { + return; + } foreach ($visibleCondition->getConditions() as $condition) { if ($condition instanceof ConditionChainInterface) { $this->matchInvisibleProperty($condition, $invisibleProperties, $environment); } - if (!\method_exists($condition, 'getPropertyName')) { + if (!method_exists($condition, 'getPropertyName')) { continue; } @@ -560,20 +588,20 @@ private function matchInvisibleProperty( /** * Match invisible sub properties. * - * @param ModelInterface $model The model. - * @param DataDefinition\Definition\Properties\PropertyInterface $property The property. - * @param PropertyValueBagInterface $propertyValueBag The property values. - * @param EnvironmentInterface $environment The environment. + * @param ModelInterface $model The model. + * @param PropertyInterface $property The property. + * @param PropertyValueBagInterface $propertyValueBag The property values. + * @param EnvironmentInterface $environment The environment. * * @return array */ private function matchInvisibleSubProperties( ModelInterface $model, - DataDefinition\Definition\Properties\PropertyInterface $property, + PropertyInterface $property, PropertyValueBagInterface $propertyValueBag, EnvironmentInterface $environment - ) { - $palettesDefinition = $environment->getDataDefinition()->getPalettesDefinition(); + ): array { + $palettesDefinition = $this->getDataDefinition($environment)->getPalettesDefinition(); $testPropertyValueBag = clone $propertyValueBag; $testPropertyValueBag->setPropertyValue('dummyNotVisible', true); @@ -586,7 +614,7 @@ private function matchInvisibleSubProperties( continue; } - $legendProperties = (array) $legend->getProperties($model, $testPropertyValueBag); + $legendProperties = $legend->getProperties($model, $testPropertyValueBag); if (empty($legendProperties)) { continue; } @@ -612,22 +640,25 @@ private function matchInvisibleSubProperties( /** * Match the parent invisible property. * - * @param ConditionInterface $visibleCondition The visible condition. - * @param DataDefinition\Definition\Properties\PropertyInterface $property The property. - * @param DataDefinition\Palette\PropertyInterface $legendProperty The legend property. - * @param array $invisibleProperties The invisible properties. - * @param EnvironmentInterface $environment The environment. + * @param ConditionInterface $visibleCondition The visible condition. + * @param PropertyInterface $property The property. + * @param DataDefinition\Palette\PropertyInterface $legendProperty The legend property. + * @param array $invisibleProperties The invisible properties. + * @param EnvironmentInterface $environment The environment. * * @return void */ private function matchParentInvisibleProperty( ConditionInterface $visibleCondition, - DataDefinition\Definition\Properties\PropertyInterface $property, + PropertyInterface $property, DataDefinition\Palette\PropertyInterface $legendProperty, array &$invisibleProperties, EnvironmentInterface $environment - ) { - $propertiesDefinition = $environment->getDataDefinition()->getPropertiesDefinition(); + ): void { + $propertiesDefinition = $this->getDataDefinition($environment)->getPropertiesDefinition(); + if (!$visibleCondition instanceof ConditionChainInterface) { + return; + } foreach ($visibleCondition->getConditions() as $condition) { if ($condition instanceof ConditionChainInterface) { @@ -641,7 +672,7 @@ private function matchParentInvisibleProperty( } if ( - !\method_exists($condition, 'getPropertyName') + !method_exists($condition, 'getPropertyName') || ($property->getName() !== $condition->getPropertyName()) ) { continue; @@ -669,20 +700,24 @@ private function matchParentInvisibleProperty( * * @return ModelInterface */ + #[ReturnTypeWillChange] protected function getIntersectionModel(Action $action, EnvironmentInterface $environment) { - $inputProvider = $environment->getInputProvider(); + $inputProvider = $this->getInputProvider($environment); $dataProvider = $environment->getDataProvider(); - $dataDefinition = $environment->getDataDefinition(); + $dataDefinition = $this->getDataDefinition($environment); $propertiesDefinition = $dataDefinition->getPropertiesDefinition(); $session = $this->getSession($action, $environment); + if (null === $dataProvider) { + throw new LogicException('No data provider found in environment.'); + } $intersectModel = $dataProvider->getEmptyModel(); $defaultPalette = null; $legendPropertyNames = $this->getLegendPropertyNames($intersectModel, $environment, $defaultPalette); - $idProperty = \method_exists($dataProvider, 'getIdProperty') ? $dataProvider->getIdProperty() : 'id'; + $idProperty = method_exists($dataProvider, 'getIdProperty') ? $dataProvider->getIdProperty() : 'id'; foreach ((array) $session['intersectValues'] as $intersectProperty => $intersectValue) { if ( ($idProperty === $intersectProperty) @@ -730,8 +765,8 @@ private function useIntersectValue( array $legendPropertyNames, EnvironmentInterface $environment, PaletteInterface $defaultPalette = null - ) { - $propertiesDefinition = $environment->getDataDefinition()->getPropertiesDefinition(); + ): bool { + $propertiesDefinition = $this->getDataDefinition($environment)->getPropertiesDefinition(); $useIntersectValue = (bool) $defaultPalette; if ($defaultPalette && !$propertiesDefinition->getProperty($intersectPropertyName)->getWidgetType()) { @@ -741,7 +776,7 @@ private function useIntersectValue( if ( $defaultPalette && (false === $useIntersectValue) - && \in_array($intersectPropertyName, $legendPropertyNames) + && in_array($intersectPropertyName, $legendPropertyNames) ) { $useIntersectValue = true; } @@ -764,7 +799,7 @@ private function intersectModelSetPrimaryId( $intersectModel, $idProperty, EnvironmentInterface $environment - ) { + ): void { if (null !== $intersectModel->getId()) { return; } @@ -785,9 +820,9 @@ private function intersectModelSetPrimaryId( * * @throws DcGeneralInvalidArgumentException Invalid configuration. Child condition must be defined. */ - private function intersectModelSetParentId(ModelInterface $intersectModel, EnvironmentInterface $environment) + private function intersectModelSetParentId(ModelInterface $intersectModel, EnvironmentInterface $environment): void { - $dataDefinition = $environment->getDataDefinition(); + $dataDefinition = $this->getDataDefinition($environment); $parentDataDefinition = $environment->getParentDataDefinition(); if (null === $parentDataDefinition) { @@ -805,7 +840,7 @@ private function intersectModelSetParentId(ModelInterface $intersectModel, Envir $parentField = null; foreach ($childCondition->getSetters() as $setter) { - if (!\array_key_exists('to_field', $setter)) { + if (!array_key_exists('to_field', $setter)) { continue; } @@ -816,7 +851,7 @@ private function intersectModelSetParentId(ModelInterface $intersectModel, Envir if (null !== $parentField) { $intersectModel->setProperty( $parentField, - ModelId::fromSerialized($environment->getInputProvider()->getParameter('pid')) + ModelId::fromSerialized($this->getInputProvider($environment)->getParameter('pid')) ->getId() ); } @@ -835,12 +870,12 @@ private function getLegendPropertyNames( ModelInterface $intersectModel, EnvironmentInterface $environment, PaletteInterface &$defaultPalette = null - ) { - $inputProvider = $environment->getInputProvider(); - $palettesDefinition = $environment->getDataDefinition()->getPalettesDefinition(); + ): array { + $inputProvider = $this->getInputProvider($environment); + $palettesDefinition = $this->getDataDefinition($environment)->getPalettesDefinition(); $legendPropertyNames = []; - if ($inputProvider->hasValue('FORM_INPUTS') && (1 === \count($palettesDefinition->getPalettes()))) { + if ($inputProvider->hasValue('FORM_INPUTS') && (1 === count($palettesDefinition->getPalettes()))) { return $legendPropertyNames; } @@ -860,6 +895,7 @@ private function getLegendPropertyNames( * * @return array */ + #[ReturnTypeWillChange] abstract protected function getSession(Action $action, EnvironmentInterface $environment); /** @@ -870,5 +906,66 @@ abstract protected function getSession(Action $action, EnvironmentInterface $env * * @return array */ + #[ReturnTypeWillChange] abstract protected function getPropertiesFromSession(Action $action, EnvironmentInterface $environment); + + /** + * Get property value bag from the model. + * + * @param Action $action The action. + * @param ModelInterface $model The model. + * @param EnvironmentInterface $environment The environment. + * + * @return PropertyValueBagInterface + * + * @throws DcGeneralInvalidArgumentException If create property value bug, the construct argument isn´t right. + * + * @SuppressWarnings(PHPMD.Superglobals) + */ + abstract protected function getPropertyValueBagFromModel( + Action $action, + ModelInterface $model, + EnvironmentInterface $environment + ); + + private function getDataDefinition(EnvironmentInterface $environment): ContainerInterface + { + $definition = $environment->getDataDefinition(); + if (null === $definition) { + throw new LogicException('No data definition found in environment.'); + } + + return $definition; + } + + private function getEventDispatcher(EnvironmentInterface $environment): EventDispatcherInterface + { + $dispatcher = $environment->getEventDispatcher(); + if (null === $dispatcher) { + throw new LogicException('No event dispatcher found in environment.'); + } + + return $dispatcher; + } + + private function getInputProvider(EnvironmentInterface $environment): InputProviderInterface + { + $input = $environment->getInputProvider(); + if (null === $input) { + throw new LogicException('No input provider found in environment.'); + } + + return $input; + } + + private function getTranslator(EnvironmentInterface $environment): TranslatorInterface + { + $translator = $environment->getTranslator(); + + if (null === $translator) { + throw new LogicException('No translator found in environment.'); + } + + return $translator; + } } From c7910fe1266d841d96307922175029470a0bef61 Mon Sep 17 00:00:00 2001 From: Christian Schiffler Date: Wed, 6 Sep 2023 21:58:06 +0200 Subject: [PATCH 41/53] Do not enable composer plugins --- composer.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/composer.json b/composer.json index bd34f8f7..01b6afaf 100644 --- a/composer.json +++ b/composer.json @@ -89,8 +89,8 @@ "config": { "allow-plugins": { "contao-components/installer": false, - "contao/manager-plugin": true, - "php-http/discovery": true + "contao/manager-plugin": false, + "php-http/discovery": false } }, "extra": { From bbfeaf4c1ed7c10ba71fc97903240d30c67263a1 Mon Sep 17 00:00:00 2001 From: Ingolf Steinhardt Date: Wed, 6 Sep 2023 22:55:31 +0200 Subject: [PATCH 42/53] Any fixes --- .../Callback/ModelLabelCallbackListener.php | 6 ++- .../Event/ModelToLabelEvent.php | 4 +- .../View/Contao2BackendView/TreePicker.php | 41 ++++++++----------- .../View/Contao2BackendView/TreeView.php | 2 +- src/Controller/Ajax3X.php | 10 ++++- 5 files changed, 32 insertions(+), 31 deletions(-) diff --git a/src/Contao/Callback/ModelLabelCallbackListener.php b/src/Contao/Callback/ModelLabelCallbackListener.php index cf99209e..c4e478ba 100644 --- a/src/Contao/Callback/ModelLabelCallbackListener.php +++ b/src/Contao/Callback/ModelLabelCallbackListener.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -94,6 +95,7 @@ private function updateNonTableMode(ModelToLabelEvent $event, ?string $value): v $value ); + assert(\is_string($value)); $event->setLabel($value); } diff --git a/src/Contao/View/Contao2BackendView/Event/ModelToLabelEvent.php b/src/Contao/View/Contao2BackendView/Event/ModelToLabelEvent.php index 9d35fda0..c8c110dc 100644 --- a/src/Contao/View/Contao2BackendView/Event/ModelToLabelEvent.php +++ b/src/Contao/View/Contao2BackendView/Event/ModelToLabelEvent.php @@ -40,7 +40,7 @@ class ModelToLabelEvent extends AbstractModelAwareEvent * * @var string */ - protected $label; + protected $label = ''; /** * The label information instance. @@ -54,7 +54,7 @@ class ModelToLabelEvent extends AbstractModelAwareEvent * * @var array */ - protected $args; + protected $args = []; /** * Set the arguments to use when generating the final string representation using the format string. diff --git a/src/Contao/View/Contao2BackendView/TreePicker.php b/src/Contao/View/Contao2BackendView/TreePicker.php index bc40dd65..d01ad303 100644 --- a/src/Contao/View/Contao2BackendView/TreePicker.php +++ b/src/Contao/View/Contao2BackendView/TreePicker.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2022 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -19,7 +19,7 @@ * @author Ingolf Steinhardt * @author David Molineus * @author Kim Wormer - * @copyright 2013-2022 Contao Community Alliance. + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -1204,12 +1204,15 @@ protected function getFormatter(ModelInterface $model, $treeMode) public function formatModel(ModelInterface $model, $treeMode = true) { /** @var ListingConfigInterface $listing */ - $environment = $this->getEnvironment(); - $definition = $environment->getDataDefinition(); - $listing = $definition->getDefinition(Contao2BackendViewDefinitionInterface::NAME)->getListingConfig(); - $properties = $definition->getPropertiesDefinition(); - $firstSorting = \reset(\array_keys((array) $listing->getDefaultSortingFields())); - $formatter = $this->getFormatter($model, $treeMode); + $environment = $this->getEnvironment(); + $definition = $environment->getDataDefinition(); + $listing = $definition + ->getDefinition(Contao2BackendViewDefinitionInterface::NAME) + ->getListingConfig(); + $properties = $definition->getPropertiesDefinition(); + $defaultSortFields = \array_keys((array) $listing->getDefaultSortingFields()); + $firstSorting = \reset($defaultSortFields); + $formatter = $this->getFormatter($model, $treeMode); $arguments = []; foreach ($formatter->getPropertyNames() as $propertyName) { @@ -1259,20 +1262,12 @@ private function prepareLabelWithDisplayedProperties( $fieldList = $formatter->getPropertyNames(); - if (!\is_array($arguments)) { + foreach ($fieldList as $j => $propertyName) { $labelList[] = [ - 'colspan' => \count($fieldList), - 'class' => 'tl_file_list col_1', - 'content' => $arguments + 'colspan' => 1, + 'class' => 'tl_file_list col_' . $j . (($propertyName === $firstSorting) ? ' ordered_by' : ''), + 'content' => ('' !== $arguments[$propertyName]) ? $arguments[$propertyName] : '-' ]; - } else { - foreach ($fieldList as $j => $propertyName) { - $labelList[] = [ - 'colspan' => 1, - 'class' => 'tl_file_list col_' . $j . (($propertyName === $firstSorting) ? ' ordered_by' : ''), - 'content' => ('' !== $arguments[$propertyName]) ? $arguments[$propertyName] : '-' - ]; - } } } @@ -1298,11 +1293,7 @@ private function prepareLabelWithOutDisplayedProperties( return; } - if (!\is_array($arguments)) { - $string = $arguments; - } else { - $string = \vsprintf($label, $arguments); - } + $string = \vsprintf($label, $arguments); if (($maxLength = null !== $formatter->getMaxLength()) && \strlen($string) > $maxLength) { $string = \substr($string, 0, $maxLength); diff --git a/src/Contao/View/Contao2BackendView/TreeView.php b/src/Contao/View/Contao2BackendView/TreeView.php index 884cfefd..3a05c6fb 100644 --- a/src/Contao/View/Contao2BackendView/TreeView.php +++ b/src/Contao/View/Contao2BackendView/TreeView.php @@ -649,7 +649,7 @@ private function languageSwitcher(EnvironmentInterface $environment) ->set('languages', $environment->getController()->getSupportedLanguages(null)) ->set('language', $dataProvider->getCurrentLanguage()) ->set('submit', $this->environment->getTranslator()->translate('MSC.showSelected')) - ->set('REQUEST_TOKEN', REQUEST_TOKEN) + ->set('REQUEST_TOKEN', $this->tokenManager->getToken($this->tokenName)) ->parse(); } diff --git a/src/Controller/Ajax3X.php b/src/Controller/Ajax3X.php index e244b7e6..8070f11e 100644 --- a/src/Controller/Ajax3X.php +++ b/src/Controller/Ajax3X.php @@ -27,6 +27,7 @@ use Contao\CoreBundle\Exception\ResponseException; use Contao\Dbafs; +use Contao\PageSelector; use Contao\StringUtil; use Contao\Widget; use ContaoCommunityAlliance\Contao\Bindings\ContaoEvents; @@ -35,6 +36,8 @@ use ContaoCommunityAlliance\DcGeneral\Data\ModelId; use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; use ContaoCommunityAlliance\DcGeneral\Data\PropertyValueBag; +use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; +use ContaoCommunityAlliance\DcGeneral\SessionStorageInterface; use Symfony\Component\HttpFoundation\Response; /** @@ -99,6 +102,11 @@ protected function loadPagetree() $environment = $this->getEnvironment(); $input = $environment->getInputProvider(); $session = $environment->getSessionStorage(); + + assert($input instanceof InputProviderInterface); + + assert($session instanceof SessionStorageInterface); + $field = $input->getValue('field'); $name = $input->getValue('name'); $level = (int) $input->getValue('level'); @@ -120,7 +128,7 @@ protected function loadPagetree() $arrData['id'] = $ajaxName ?: $rootId; $arrData['name'] = $name; - /** @var \PageSelector $widget */ + /** @var PageSelector $widget */ $widget = new $GLOBALS['BE_FFL']['pageSelector']($arrData, $this->getDataContainer()); $widget->value = $this->getTreeValue('page', $input->getValue('value')); From 9b54fa055d92db1f016863afe1e1e7b30881baf6 Mon Sep 17 00:00:00 2001 From: Ingolf Steinhardt Date: Wed, 6 Sep 2023 23:15:29 +0200 Subject: [PATCH 43/53] Any fixes --- src/Data/PropertyValueBag.php | 8 ++++---- .../Definition/DefaultDataProviderDefinition.php | 10 +++++----- .../Definition/DefaultPropertiesDefinition.php | 7 ++++--- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/src/Data/PropertyValueBag.php b/src/Data/PropertyValueBag.php index 64740cc1..166592c9 100644 --- a/src/Data/PropertyValueBag.php +++ b/src/Data/PropertyValueBag.php @@ -214,7 +214,7 @@ public function getInvalidPropertyErrors() /** * {@inheritdoc} */ - public function getIterator() + public function getIterator(): \Traversable { return new \ArrayIterator($this->properties); } @@ -238,7 +238,7 @@ public function offsetExists($offset): bool /** * {@inheritdoc} */ - public function offsetGet($offset) + public function offsetGet($offset): mixed { return $this->getPropertyValue($offset); } @@ -246,7 +246,7 @@ public function offsetGet($offset) /** * {@inheritdoc} */ - public function offsetSet($offset, $value) + public function offsetSet($offset, $value): void { $this->setPropertyValue($offset, $value); } @@ -254,7 +254,7 @@ public function offsetSet($offset, $value) /** * {@inheritdoc} */ - public function offsetUnset($offset) + public function offsetUnset($offset): void { $this->removePropertyValue($offset); } diff --git a/src/DataDefinition/Definition/DefaultDataProviderDefinition.php b/src/DataDefinition/Definition/DefaultDataProviderDefinition.php index 7761c5f6..18bc04a6 100644 --- a/src/DataDefinition/Definition/DefaultDataProviderDefinition.php +++ b/src/DataDefinition/Definition/DefaultDataProviderDefinition.php @@ -130,7 +130,7 @@ public function getProviderNames() /** * {@inheritdoc} */ - public function getIterator() + public function getIterator(): \Traversable { return new \ArrayIterator($this->information); } @@ -146,7 +146,7 @@ public function count(): int /** * {@inheritdoc} */ - public function offsetExists($offset) + public function offsetExists($offset): bool { return $this->hasInformation($offset); } @@ -154,7 +154,7 @@ public function offsetExists($offset) /** * {@inheritdoc} */ - public function offsetGet($offset) + public function offsetGet($offset): mixed { return $this->getInformation($offset); } @@ -162,7 +162,7 @@ public function offsetGet($offset) /** * {@inheritdoc} */ - public function offsetSet($offset, $value) + public function offsetSet($offset, $value): void { $this->setInformation($offset, $value); } @@ -170,7 +170,7 @@ public function offsetSet($offset, $value) /** * {@inheritdoc} */ - public function offsetUnset($offset) + public function offsetUnset($offset): void { $this->removeInformation($offset); } diff --git a/src/DataDefinition/Definition/DefaultPropertiesDefinition.php b/src/DataDefinition/Definition/DefaultPropertiesDefinition.php index 3cd2d542..0f655987 100644 --- a/src/DataDefinition/Definition/DefaultPropertiesDefinition.php +++ b/src/DataDefinition/Definition/DefaultPropertiesDefinition.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -15,7 +15,8 @@ * @author Tristan Lins * @author David Molineus * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -119,7 +120,7 @@ public function getProperty($name) /** * {@inheritdoc} */ - public function getIterator() + public function getIterator(): \Traversable { return new \ArrayIterator($this->properties); } From c8a1572f0cf6a6eef6d9e9e41a52ba1254495225 Mon Sep 17 00:00:00 2001 From: Ingolf Steinhardt Date: Wed, 6 Sep 2023 23:26:58 +0200 Subject: [PATCH 44/53] Any fixes --- .../Definition/View/DefaultGroupAndSortingDefinition.php | 6 +++--- .../View/DefaultGroupAndSortingDefinitionCollection.php | 7 ++++--- src/DataDefinition/Definition/View/DefaultPanelRow.php | 7 ++++--- .../Definition/View/DefaultPanelRowCollection.php | 6 +++--- .../View/GroupAndSortingDefinitionCollectionInterface.php | 6 ++++-- 5 files changed, 18 insertions(+), 14 deletions(-) diff --git a/src/DataDefinition/Definition/View/DefaultGroupAndSortingDefinition.php b/src/DataDefinition/Definition/View/DefaultGroupAndSortingDefinition.php index 144633fc..aceaf47c 100644 --- a/src/DataDefinition/Definition/View/DefaultGroupAndSortingDefinition.php +++ b/src/DataDefinition/Definition/View/DefaultGroupAndSortingDefinition.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,7 @@ * @package contao-community-alliance/dc-general * @author Christian Schiffler * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -111,7 +111,7 @@ public function setName($name) /** * {@inheritDoc} */ - public function getIterator() + public function getIterator(): \Traversable { return new \ArrayIterator($this->information); } diff --git a/src/DataDefinition/Definition/View/DefaultGroupAndSortingDefinitionCollection.php b/src/DataDefinition/Definition/View/DefaultGroupAndSortingDefinitionCollection.php index 61598f88..62ee4ae9 100644 --- a/src/DataDefinition/Definition/View/DefaultGroupAndSortingDefinitionCollection.php +++ b/src/DataDefinition/Definition/View/DefaultGroupAndSortingDefinitionCollection.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2020 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,8 @@ * @package contao-community-alliance/dc-general * @author Christian Schiffler * @author Sven Baumann - * @copyright 2013-2020 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -152,7 +153,7 @@ public function getDefaultIndex() /** * {@inheritDoc} */ - public function getIterator() + public function getIterator(): \Traversable { return new \ArrayIterator($this->information); } diff --git a/src/DataDefinition/Definition/View/DefaultPanelRow.php b/src/DataDefinition/Definition/View/DefaultPanelRow.php index 44c2d53f..451b28dd 100644 --- a/src/DataDefinition/Definition/View/DefaultPanelRow.php +++ b/src/DataDefinition/Definition/View/DefaultPanelRow.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -161,7 +162,7 @@ public function getElement($indexOrName) /** * {@inheritDoc} */ - public function getIterator() + public function getIterator(): \Traversable { return new \ArrayIterator($this->elements); } diff --git a/src/DataDefinition/Definition/View/DefaultPanelRowCollection.php b/src/DataDefinition/Definition/View/DefaultPanelRowCollection.php index 5f301a7c..cb1bc81e 100644 --- a/src/DataDefinition/Definition/View/DefaultPanelRowCollection.php +++ b/src/DataDefinition/Definition/View/DefaultPanelRowCollection.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,7 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -101,7 +101,7 @@ public function getRow($index) /** * {@inheritDoc} */ - public function getIterator() + public function getIterator(): \Traversable { return new \ArrayIterator($this->rows); } diff --git a/src/DataDefinition/Definition/View/GroupAndSortingDefinitionCollectionInterface.php b/src/DataDefinition/Definition/View/GroupAndSortingDefinitionCollectionInterface.php index 1e5a3a0d..3148992d 100644 --- a/src/DataDefinition/Definition/View/GroupAndSortingDefinitionCollectionInterface.php +++ b/src/DataDefinition/Definition/View/GroupAndSortingDefinitionCollectionInterface.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2020 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,7 @@ * @package contao-community-alliance/dc-general * @author Christian Schiffler * @author Sven Baumann - * @copyright 2013-2020 Contao Community Alliance. + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -23,6 +23,8 @@ /** * This interface defines a collection of grouping and sorting information for the view. * + * @extends \IteratorAggregate + * * @SuppressWarnings(PHPMD.LongClassName) */ interface GroupAndSortingDefinitionCollectionInterface extends \IteratorAggregate From a23089b0a89a0d311bb35e60b0de607588409fe1 Mon Sep 17 00:00:00 2001 From: Ingolf Steinhardt Date: Wed, 6 Sep 2023 23:33:09 +0200 Subject: [PATCH 45/53] Any fixes --- src/Data/DefaultCollection.php | 6 +++--- src/Panel/DefaultPanel.php | 2 +- src/Panel/DefaultPanelContainer.php | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Data/DefaultCollection.php b/src/Data/DefaultCollection.php index c9138549..e523c63f 100644 --- a/src/Data/DefaultCollection.php +++ b/src/Data/DefaultCollection.php @@ -77,7 +77,7 @@ public function offsetExists($offset): bool /** * {@inheritdoc} */ - public function offsetGet($offset) + public function offsetGet($offset): mixed { return $this->get($offset); } @@ -85,7 +85,7 @@ public function offsetGet($offset) /** * {@inheritdoc} */ - public function offsetSet($offset, $value) + public function offsetSet($offset, $value): void { $this->arrCollection[$offset] = $value; } @@ -93,7 +93,7 @@ public function offsetSet($offset, $value) /** * {@inheritdoc} */ - public function offsetUnset($offset) + public function offsetUnset($offset): void { unset($this->arrCollection[$offset]); } diff --git a/src/Panel/DefaultPanel.php b/src/Panel/DefaultPanel.php index 7ea68984..84506985 100644 --- a/src/Panel/DefaultPanel.php +++ b/src/Panel/DefaultPanel.php @@ -102,7 +102,7 @@ public function initialize(ConfigInterface $config, PanelElementInterface $eleme /** * {@inheritdoc} */ - public function getIterator() + public function getIterator(): \Traversable { return new \ArrayIterator($this->arrElements); } diff --git a/src/Panel/DefaultPanelContainer.php b/src/Panel/DefaultPanelContainer.php index 4bda3f35..71ae322a 100644 --- a/src/Panel/DefaultPanelContainer.php +++ b/src/Panel/DefaultPanelContainer.php @@ -102,7 +102,7 @@ public function updateValues() /** * {@inheritdoc} */ - public function getIterator() + public function getIterator(): \Traversable { return new \ArrayIterator($this->arrPanels); } From 4c57835219fece805b2367c24b56e5f7d4378968 Mon Sep 17 00:00:00 2001 From: Ingolf Steinhardt Date: Wed, 6 Sep 2023 23:39:51 +0200 Subject: [PATCH 46/53] Any fixes --- .../FilterBuilder/FilterBuilderWithChildren.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/DataDefinition/ModelRelationship/FilterBuilder/FilterBuilderWithChildren.php b/src/DataDefinition/ModelRelationship/FilterBuilder/FilterBuilderWithChildren.php index 8e27c64d..47d364ec 100644 --- a/src/DataDefinition/ModelRelationship/FilterBuilder/FilterBuilderWithChildren.php +++ b/src/DataDefinition/ModelRelationship/FilterBuilder/FilterBuilderWithChildren.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2034 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,7 @@ * @author Christian Schiffler * @author Sven Baumann * @author Ingolf Steinhardt - * @copyright 2013-2034 Contao Community Alliance. + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -128,7 +128,7 @@ public function next(): ?BaseFilterBuilder * * @return mixed scalar on success, or null on failure. */ - public function key() + public function key(): mixed { return $this->index; } From 58d47fd1083e129c08c7ca800809f3f6a4a3a307 Mon Sep 17 00:00:00 2001 From: Christian Schiffler Date: Wed, 6 Sep 2023 23:41:06 +0200 Subject: [PATCH 47/53] Fix some more psalm issues --- src/Data/CollectionInterface.php | 76 ++++++------ src/Data/DefaultCollection.php | 203 +++++++++++-------------------- 2 files changed, 109 insertions(+), 170 deletions(-) diff --git a/src/Data/CollectionInterface.php b/src/Data/CollectionInterface.php index dc9c0338..85297088 100644 --- a/src/Data/CollectionInterface.php +++ b/src/Data/CollectionInterface.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,73 +14,79 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ namespace ContaoCommunityAlliance\DcGeneral\Data; +use ArrayAccess; use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralRuntimeException; +use Countable; +use IteratorAggregate; /** * This interface represents an iterable collection of Model elements. + * + * @extends IteratorAggregate + * @extends ArrayAccess */ -interface CollectionInterface extends \IteratorAggregate, \ArrayAccess, \Countable +interface CollectionInterface extends IteratorAggregate, ArrayAccess, Countable { /** * Get length of this collection. * * @return int */ - public function length(); + public function length(): int; /** * Get the model at a specific index. * - * @param int $intIndex The index of the model to retrieve. + * @param int $index The index of the model to retrieve. * - * @return ModelInterface + * @return ModelInterface|null */ - public function get($intIndex); + public function get($index): ?ModelInterface; /** * Append a model to the end of this collection. * - * @param ModelInterface $objModel The model to append to the collection. + * @param ModelInterface $model The model to append to the collection. * * @return void * * @throws DcGeneralRuntimeException When no model has been passed. */ - public function push(ModelInterface $objModel); + public function push(ModelInterface $model): void; /** * Remove the model at the end of the collection and return it. * * If the collection is empty, null will be returned. * - * @return ModelInterface + * @return ModelInterface|null */ - public function pop(); + public function pop(): ?ModelInterface; /** * Insert a model at the beginning of the collection. * - * @param ModelInterface $objModel The model to insert into the collection. + * @param ModelInterface $model The model to insert into the collection. * * @return void */ - public function unshift(ModelInterface $objModel); + public function unshift(ModelInterface $model): void; /** * Remove the model from the beginning of the collection and return it. * * If the collection is empty, null will be returned. * - * @return ModelInterface + * @return ModelInterface|null */ - public function shift(); + public function shift(): ?ModelInterface; /** * Insert a record at the specific position. @@ -88,23 +94,23 @@ public function shift(); * Move all records at position >= $index one index up. * If $index is out of bounds, just add at the end (does not fill with empty records!). * - * @param int $intIndex The index where the model shall be placed. - * @param ModelInterface $objModel The model to insert. + * @param int $index The index where the model shall be placed. + * @param ModelInterface $model The model to insert. * * @return void */ - public function insert($intIndex, ModelInterface $objModel); + public function insert($index, ModelInterface $model): void; /** * Remove the given index or model from the collection and renew the index. * * ATTENTION: Don't use key to unset in foreach because of the new index. * - * @param mixed $mixedValue The index (integer) or InterfaceGeneralModel instance to remove. + * @param int|ModelInterface $mixedValue The index or instance to remove. * * @return void */ - public function remove($mixedValue); + public function remove($mixedValue): void; /** * Remove the model with the given id from the collection. @@ -113,7 +119,7 @@ public function remove($mixedValue); * * @return void */ - public function removeById($modelId); + public function removeById($modelId): void; /** * Check whether the given model is contained in the collection. @@ -122,7 +128,7 @@ public function removeById($modelId); * * @return bool */ - public function contains($model); + public function contains($model): bool; /** * Check whether the given model is contained in the collection. @@ -131,14 +137,14 @@ public function contains($model); * * @return bool */ - public function containsById($modelId); + public function containsById($modelId): bool; /** * Retrieve the ids of the models. * - * @return array + * @return list */ - public function getModelIds(); + public function getModelIds(): array; /** * Intersect the given collection with this collection and return the result. @@ -147,7 +153,7 @@ public function getModelIds(); * * @return CollectionInterface */ - public function intersect($collection); + public function intersect($collection): CollectionInterface; /** * Compute the union of this collection and the given collection. @@ -156,7 +162,7 @@ public function intersect($collection); * * @return CollectionInterface */ - public function union($collection); + public function union($collection): CollectionInterface; /** * Computes the difference of the collection. @@ -166,30 +172,30 @@ public function union($collection); * @return CollectionInterface The collection containing all the entries from this collection that are not present * in the given collection. */ - public function diff($collection); + public function diff($collection): CollectionInterface; /** * Check if the given collection is an subset of the given collection. * * @param CollectionInterface $collection The collection to check. * - * @return boolean + * @return bool */ - public function isSubsetOf($collection); + public function isSubsetOf($collection): bool; /** * Make a reverse sorted collection of this collection. * - * @return ModelInterface + * @return CollectionInterface */ - public function reverse(); + public function reverse(): CollectionInterface; /** * Sort the records with the given callback and return the new sorted collection. * - * @param callback $callback The callback function to use. + * @param callable(ModelInterface, ModelInterface): int $callback The callback function to use. * - * @return ModelInterface + * @return CollectionInterface */ - public function sort($callback); + public function sort($callback): CollectionInterface; } diff --git a/src/Data/DefaultCollection.php b/src/Data/DefaultCollection.php index e523c63f..ce332200 100644 --- a/src/Data/DefaultCollection.php +++ b/src/Data/DefaultCollection.php @@ -26,8 +26,20 @@ namespace ContaoCommunityAlliance\DcGeneral\Data; +use ArrayIterator; use Contao\ArrayUtil; use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralRuntimeException; +use Traversable; + +use function array_key_exists; +use function array_pop; +use function array_reverse; +use function array_shift; +use function array_unshift; +use function array_values; +use function count; +use function is_object; +use function uasort; /** * Class DefaultCollection. @@ -44,18 +56,16 @@ class DefaultCollection implements CollectionInterface /** * The list of contained models. * - * @var ModelInterface[] + * @var array */ protected array $arrCollection = []; /** - * Get length of this collection. - * - * @return int + * {@inheritDoc} */ public function length(): int { - return \count($this->arrCollection); + return count($this->arrCollection); } /** @@ -71,13 +81,13 @@ public function count(): int */ public function offsetExists($offset): bool { - return \array_key_exists($offset, $this->arrCollection); + return array_key_exists($offset, $this->arrCollection); } /** * {@inheritdoc} */ - public function offsetGet($offset): mixed + public function offsetGet($offset): ?ModelInterface { return $this->get($offset); } @@ -99,15 +109,11 @@ public function offsetUnset($offset): void } /** - * Get the model at a specific index. - * - * @param int $index The index of the model to retrieve. - * - * @return ModelInterface + * {@inheritDoc} */ - public function get($index) + public function get($index): ?ModelInterface { - if (\array_key_exists($index, $this->arrCollection)) { + if (array_key_exists($index, $this->arrCollection)) { return $this->arrCollection[$index]; } @@ -115,81 +121,51 @@ public function get($index) } /** - * Append a model to the end of this collection. - * - * @param ModelInterface $model The model to append to the collection. - * - * @return void - * - * @throws DcGeneralRuntimeException When no model has been passed. + * {@inheritDoc} */ - public function push(ModelInterface $model) + public function push(ModelInterface $model): void { - if (!$model) { - throw new DcGeneralRuntimeException('push() - no model passed', 1); - } - $this->arrCollection[] = $model; } /** - * Remove the model at the end of the collection and return it. - * - * If the collection is empty, null will be returned. - * - * @return ModelInterface + * {@inheritDoc} */ - public function pop() + public function pop(): ?ModelInterface { - if (\count($this->arrCollection)) { - return \array_pop($this->arrCollection); + if (count($this->arrCollection)) { + return array_pop($this->arrCollection); } return null; } /** - * Insert a model at the beginning of the collection. - * - * @param ModelInterface $model The model to insert into the collection. - * - * @return void + * {@inheritDoc} */ - public function unshift(ModelInterface $model) + public function unshift(ModelInterface $model): void { if ($model->hasProperties()) { - \array_unshift($this->arrCollection, $model); + array_unshift($this->arrCollection, $model); } } /** - * Remove the model from the beginning of the collection and return it. - * - * If the collection is empty, null will be returned. - * - * @return ModelInterface + * {@inheritDoc} */ - public function shift() + public function shift(): ?ModelInterface { - if (\count($this->arrCollection)) { - return \array_shift($this->arrCollection); + if (count($this->arrCollection)) { + return array_shift($this->arrCollection); } return null; } /** - * Insert a record at the specific position. - * - * Move all records at position >= $index one index up. - * If $index is out of bounds, just add at the end (does not fill with empty records!). - * - * @param int $index The index where the model shall be placed. - * @param ModelInterface $model The model to insert. - * - * @return void + * {@inheritDoc} */ - public function insert($index, ModelInterface $model) + public function insert($index, ModelInterface $model): void { if ($model->hasProperties()) { ArrayUtil::arrayInsert($this->arrCollection, $index, [$model]); @@ -197,35 +173,27 @@ public function insert($index, ModelInterface $model) } /** - * Remove the given index or model from the collection and renew the index. - * - * ATTENTION: Don't use key to unset in foreach because of the new index. - * - * @param mixed $indexValue The index (integer) or InterfaceGeneralModel instance to remove. - * - * @return void + * {@inheritDoc} */ - public function remove($indexValue) + public function remove($mixedValue): void { - if (\is_object($indexValue)) { + if (is_object($mixedValue)) { foreach ($this->arrCollection as $collectionIndex => $model) { - if ($indexValue === $model) { + if ($mixedValue === $model) { unset($this->arrCollection[$collectionIndex]); } } } else { - unset($this->arrCollection[$indexValue]); + unset($this->arrCollection[$mixedValue]); } - $this->arrCollection = \array_values($this->arrCollection); + $this->arrCollection = array_values($this->arrCollection); } /** - * Retrieve the ids of the models. - * - * @return array + * {@inheritDoc} */ - public function getModelIds() + public function getModelIds(): array { $ids = []; foreach ($this as $model) { @@ -237,13 +205,9 @@ public function getModelIds() } /** - * Remove the model with the given id from the collection. - * - * @param mixed $modelId The id of the model to remove. - * - * @return void + * {@inheritDoc} */ - public function removeById($modelId) + public function removeById($modelId): void { foreach ($this->arrCollection as $index => $model) { if ($modelId === $model->getId()) { @@ -253,13 +217,9 @@ public function removeById($modelId) } /** - * Check whether the given model is contained in the collection. - * - * @param ModelInterface $model The model to search. - * - * @return bool + * {@inheritDoc} */ - public function contains($model) + public function contains($model): bool { /** @var ModelInterface $localModel */ foreach ($this as $localModel) { @@ -272,13 +232,9 @@ public function contains($model) } /** - * Check whether the given model is contained in the collection. - * - * @param mixed $modelId The model id to search. - * - * @return bool + * {@inheritDoc} */ - public function containsById($modelId) + public function containsById($modelId): bool { /** @var ModelInterface $localModel */ foreach ($this as $localModel) { @@ -291,13 +247,9 @@ public function containsById($modelId) } /** - * Intersect the given collection with this collection and return the result. - * - * @param CollectionInterface $collection The collection to intersect. - * - * @return CollectionInterface + * {@inheritDoc} */ - public function intersect($collection) + public function intersect($collection): CollectionInterface { $intersection = new DefaultCollection(); /** @var ModelInterface $localModel */ @@ -317,13 +269,9 @@ public function intersect($collection) } /** - * Compute the union of this collection and the given collection. - * - * @param CollectionInterface $collection The collection to intersect. - * - * @return CollectionInterface + * {@inheritDoc} */ - public function union($collection) + public function union($collection): CollectionInterface { $union = clone $this; @@ -336,14 +284,9 @@ public function union($collection) } /** - * Computes the difference of the collection. - * - * @param CollectionInterface $collection The collection to compute the difference for. - * - * @return CollectionInterface The collection containing all the entries from this collection that are not present - * in the given collection. + * {@inheritDoc} */ - public function diff($collection) + public function diff($collection): CollectionInterface { $diff = new DefaultCollection(); /** @var ModelInterface $localModel */ @@ -364,13 +307,9 @@ public function diff($collection) } /** - * Check if the given collection is an subset of the given collection. - * - * @param CollectionInterface $collection The collection to check. - * - * @return boolean + * {@inheritDoc} */ - public function isSubsetOf($collection) + public function isSubsetOf($collection): bool { /** @var ModelInterface $localModel */ foreach ($this as $localModel) { @@ -389,43 +328,37 @@ public function isSubsetOf($collection) } /** - * Make a reverse sorted collection of this collection. - * - * @return CollectionInterface + * {@inheritDoc} */ - public function reverse() + public function reverse(): CollectionInterface { $newCollection = clone $this; - $newCollection->arrCollection = \array_reverse($this->arrCollection); + $newCollection->arrCollection = array_reverse($this->arrCollection); return $newCollection; } /** - * Sort the records with the given callback and return the new sorted collection. - * - * @param callback $callback The callback function to use. - * - * @return CollectionInterface + * {@inheritDoc} */ - public function sort($callback) + public function sort($callback): CollectionInterface { $newCollection = clone $this; - \uasort($newCollection->arrCollection, $callback); + uasort($newCollection->arrCollection, $callback); - $newCollection->arrCollection = \array_values($newCollection->arrCollection); + $newCollection->arrCollection = array_values($newCollection->arrCollection); return $newCollection; } /** - * Get a iterator for this collection. + * {@inheritDoc} * - * @return \Traversable + * @return Traversable */ - public function getIterator() + public function getIterator(): Traversable { - return new \ArrayIterator($this->arrCollection); + return new ArrayIterator($this->arrCollection); } } From 6dbdb116448a6940c4f054049c210fb0a6357d5a Mon Sep 17 00:00:00 2001 From: Ingolf Steinhardt Date: Wed, 6 Sep 2023 23:58:42 +0200 Subject: [PATCH 48/53] Any fixes --- src/Controller/Ajax3X.php | 2 ++ src/Data/DefaultFilterOptionCollection.php | 2 +- src/Data/DefaultLanguageInformationCollection.php | 2 +- src/Data/DefaultModel.php | 6 +++--- src/Data/FilterOptionCollectionInterface.php | 6 ++++-- 5 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/Controller/Ajax3X.php b/src/Controller/Ajax3X.php index 8070f11e..af0a2182 100644 --- a/src/Controller/Ajax3X.php +++ b/src/Controller/Ajax3X.php @@ -43,6 +43,8 @@ /** * Class GeneralAjax - General purpose Ajax handler for "executePostActions" in Contao 3.X as we can not use the default * Contao handling. + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class Ajax3X extends Ajax { diff --git a/src/Data/DefaultFilterOptionCollection.php b/src/Data/DefaultFilterOptionCollection.php index eb99ef0e..394fd3b7 100644 --- a/src/Data/DefaultFilterOptionCollection.php +++ b/src/Data/DefaultFilterOptionCollection.php @@ -49,7 +49,7 @@ public function add($filterKey, $filterValue) * * @return \Traversable */ - public function getIterator() + public function getIterator(): \Traversable { return new \ArrayIterator($this->filterValues); } diff --git a/src/Data/DefaultLanguageInformationCollection.php b/src/Data/DefaultLanguageInformationCollection.php index 178b44ec..ab9ad230 100644 --- a/src/Data/DefaultLanguageInformationCollection.php +++ b/src/Data/DefaultLanguageInformationCollection.php @@ -48,7 +48,7 @@ public function add(LanguageInformationInterface $language) * * @return \ArrayIterator */ - public function getIterator() + public function getIterator(): \Traversable { return new \ArrayIterator($this->languages); } diff --git a/src/Data/DefaultModel.php b/src/Data/DefaultModel.php index 633b2254..85245918 100644 --- a/src/Data/DefaultModel.php +++ b/src/Data/DefaultModel.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -17,7 +17,7 @@ * @author Andreas Isaak * @author Patrick Kahl * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -205,7 +205,7 @@ public function hasProperties() * * @return \ArrayIterator */ - public function getIterator() + public function getIterator(): \Traversable { return new \ArrayIterator($this->arrProperties); } diff --git a/src/Data/FilterOptionCollectionInterface.php b/src/Data/FilterOptionCollectionInterface.php index 6749a110..60265f53 100644 --- a/src/Data/FilterOptionCollectionInterface.php +++ b/src/Data/FilterOptionCollectionInterface.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,7 @@ * @author Christian Schiffler * @author Stefan Heimes * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -23,6 +23,8 @@ /** * This represents an iterable collection of Model elements. + * + * @extends \IteratorAggregate */ interface FilterOptionCollectionInterface extends \IteratorAggregate, \Countable { From 1d539971ec4b683f1e703183656bf941b6bb90ad Mon Sep 17 00:00:00 2001 From: Christian Schiffler Date: Thu, 7 Sep 2023 00:01:15 +0200 Subject: [PATCH 49/53] Fix some more issues --- ...AbstractPropertyOverrideEditAllHandler.php | 27 +++++++++++++------ 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/src/View/ActionHandler/AbstractPropertyOverrideEditAllHandler.php b/src/View/ActionHandler/AbstractPropertyOverrideEditAllHandler.php index f09946b6..7b2a6051 100644 --- a/src/View/ActionHandler/AbstractPropertyOverrideEditAllHandler.php +++ b/src/View/ActionHandler/AbstractPropertyOverrideEditAllHandler.php @@ -41,6 +41,7 @@ use ContaoCommunityAlliance\DcGeneral\Event\PostPersistModelEvent; use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralInvalidArgumentException; use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralRuntimeException; +use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; use ContaoCommunityAlliance\DcGeneral\SessionStorageInterface; use LogicException; @@ -65,8 +66,8 @@ abstract class AbstractPropertyOverrideEditAllHandler extends AbstractPropertyVi */ protected function handleSubmit(Action $action, EnvironmentInterface $environment) { - $inputProvider = $environment->getInputProvider(); - $sessionStorage = $environment->getSessionStorage(); + $inputProvider = $this->getInputProvider($environment); + $sessionStorage = $this->getSessionStorage($environment); $eventDispatcher = $environment->getEventDispatcher(); if ( @@ -268,7 +269,7 @@ function ($error) use ( if ($modelEditError && isset($modelEditError[$errorPropertyName])) { $propertyValueBag->setPropertyValue( $errorPropertyName, - $environment->getInputProvider()->getValue($errorPropertyName) + $this->getInputProvider($environment)->getValue($errorPropertyName) ); $propertyValueBag->markPropertyValueAsInvalid( @@ -347,7 +348,7 @@ private function handleEditHandler( PropertyValueBagInterface $propertyValueBag, EnvironmentInterface $environment ) { - $inputProvider = $environment->getInputProvider(); + $inputProvider = $this->getInputProvider($environment); $editInformation = System::getContainer()->get('cca.dc-general.edit-information'); $inputValues = $this->handleInputValues($action, $model, $environment); @@ -405,7 +406,7 @@ private function handleInputValues(Action $action, ModelInterface $model, Enviro return []; } - $inputProvider = $environment->getInputProvider(); + $inputProvider = $this->getInputProvider($environment); $inputValues = []; foreach (\array_keys($_POST) as $valueName) { @@ -460,7 +461,7 @@ protected function restoreInputValues( return; } - $inputProvider = $environment->getInputProvider(); + $inputProvider = $this->getInputProvider($environment); unset($_POST); foreach (\array_keys($inputValues) as $postName) { @@ -589,8 +590,8 @@ protected function getPropertyValueBagFromModel( */ protected function getCollectionFromSession(Action $action, EnvironmentInterface $environment) { - $inputProvider = $environment->getInputProvider(); - $sessionStorage = $environment->getSessionStorage(); + $inputProvider = $this->getInputProvider($environment); + $sessionStorage = $this->getSessionStorage($environment); $dataDefinition = $this->getDataDefinition($environment); $dataProvider = $environment->getDataProvider($dataDefinition->getName()); @@ -796,6 +797,16 @@ protected function getPropertiesFromSession(Action $action, EnvironmentInterface return $properties; } + private function getInputProvider(EnvironmentInterface $environment): InputProviderInterface + { + $inputProvider = $environment->getInputProvider(); + if (null === $inputProvider) { + throw new LogicException('No input provider found in environment.'); + } + + return $inputProvider; + } + private function getSessionStorage(EnvironmentInterface $environment): SessionStorageInterface { $sessionStorage = $environment->getSessionStorage(); From 35a226f8dc1050e5a86d106491d4f88ffc108fab Mon Sep 17 00:00:00 2001 From: Ingolf Steinhardt Date: Thu, 7 Sep 2023 00:04:41 +0200 Subject: [PATCH 50/53] Any fixes --- .../ActionHandler/MultipleHandler/PasteAllHandler.php | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/PasteAllHandler.php b/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/PasteAllHandler.php index e8f8d568..3b34d451 100644 --- a/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/PasteAllHandler.php +++ b/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/PasteAllHandler.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2020 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -12,7 +12,8 @@ * * @package contao-community-alliance/dc-general * @author Sven Baumann - * @copyright 2013-2020 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0 * @filesource */ @@ -73,7 +74,10 @@ public function __construct(RequestScopeDeterminator $scopeDeterminator) */ public function handleEvent(ActionEvent $event) { - if (!$this->getScopeDeterminator()->currentScopeIsBackend() || ('pasteAll' !== $event->getAction()->getName())) { + if ( + !$this->getScopeDeterminator()->currentScopeIsBackend() + || ('pasteAll' !== $event->getAction()->getName()) + ) { return; } From bf20104af2538df80105802c7109dc9f36b64c92 Mon Sep 17 00:00:00 2001 From: Christian Schiffler Date: Thu, 7 Sep 2023 00:14:17 +0200 Subject: [PATCH 51/53] Fix unit test to reflect changes in treeview --- tests/Contao/Event/SubscriberTest.php | 33 ++++++++++++++++----------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/tests/Contao/Event/SubscriberTest.php b/tests/Contao/Event/SubscriberTest.php index 455f2d61..6594b956 100644 --- a/tests/Contao/Event/SubscriberTest.php +++ b/tests/Contao/Event/SubscriberTest.php @@ -57,6 +57,7 @@ use ContaoCommunityAlliance\DcGeneral\View\Event\RenderReadablePropertyValueEvent; use ContaoCommunityAlliance\Translator\TranslatorChain; use Symfony\Component\EventDispatcher\EventDispatcher; +use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface; /** * This class test the subscriber. @@ -472,79 +473,85 @@ public function testInitTwig() public function initializePanelsDataProvider() { + $treeConstructorArgs = [ + $this->mockScopeDeterminator(), + $this->getMockForAbstractClass(CsrfTokenManagerInterface::class), + 'csrf-token-name' + ]; + return [ ['select', NonBaseView::class, [], [1, 2]], ['select', BaseView::class, [$this->mockScopeDeterminator()], [1, 2]], ['select', ListView::class, [$this->mockScopeDeterminator()], [1, 2]], ['select', ParentView::class, [$this->mockScopeDeterminator()], [1, 2]], - ['select', TreeView::class, [$this->mockScopeDeterminator()], [1, 2]], + ['select', TreeView::class, $treeConstructorArgs, [1, 2]], ['copy', NonBaseView::class, [], [1, 2]], ['copy', BaseView::class, [$this->mockScopeDeterminator()], [3, 4]], ['copy', ListView::class, [$this->mockScopeDeterminator()], [3, 4]], ['copy', ParentView::class, [$this->mockScopeDeterminator()], [3, 4]], - ['copy', TreeView::class, [$this->mockScopeDeterminator()], [3, 4]], + ['copy', TreeView::class, $treeConstructorArgs, [3, 4]], ['create', NonBaseView::class, [], [1, 2]], ['create', BaseView::class, [$this->mockScopeDeterminator()], [3, 4]], ['create', ListView::class, [$this->mockScopeDeterminator()], [3, 4]], ['create', ParentView::class, [$this->mockScopeDeterminator()], [3, 4]], - ['create', TreeView::class, [$this->mockScopeDeterminator()], [3, 4]], + ['create', TreeView::class, $treeConstructorArgs, [3, 4]], ['paste', NonBaseView::class, [], [1, 2]], ['paste', BaseView::class, [$this->mockScopeDeterminator()], [3, 4]], ['paste', ListView::class, [$this->mockScopeDeterminator()], [3, 4]], ['paste', ParentView::class, [$this->mockScopeDeterminator()], [3, 4]], - ['paste', TreeView::class, [$this->mockScopeDeterminator()], [3, 4]], + ['paste', TreeView::class, $treeConstructorArgs, [3, 4]], ['delete', NonBaseView::class, [], [1, 2]], ['delete', BaseView::class, [$this->mockScopeDeterminator()], [3, 4]], ['delete', ListView::class, [$this->mockScopeDeterminator()], [3, 4]], ['delete', ParentView::class, [$this->mockScopeDeterminator()], [3, 4]], - ['delete', TreeView::class, [$this->mockScopeDeterminator()], [3, 4]], + ['delete', TreeView::class, $treeConstructorArgs, [3, 4]], ['move', NonBaseView::class, [], [1, 2]], ['move', BaseView::class, [$this->mockScopeDeterminator()], [3, 4]], ['move', ListView::class, [$this->mockScopeDeterminator()], [3, 4]], ['move', ParentView::class, [$this->mockScopeDeterminator()], [3, 4]], - ['move', TreeView::class, [$this->mockScopeDeterminator()], [3, 4]], + ['move', TreeView::class, $treeConstructorArgs, [3, 4]], ['undo', NonBaseView::class, [], [1, 2]], ['undo', BaseView::class, [$this->mockScopeDeterminator()], [3, 4]], ['undo', ListView::class, [$this->mockScopeDeterminator()], [3, 4]], ['undo', ParentView::class, [$this->mockScopeDeterminator()], [3, 4]], - ['undo', TreeView::class, [$this->mockScopeDeterminator()], [3, 4]], + ['undo', TreeView::class, $treeConstructorArgs, [3, 4]], ['edit', NonBaseView::class, [], [1, 2]], ['edit', BaseView::class, [$this->mockScopeDeterminator()], [3, 4]], ['edit', ListView::class, [$this->mockScopeDeterminator()], [3, 4]], ['edit', ParentView::class, [$this->mockScopeDeterminator()], [3, 4]], - ['edit', TreeView::class, [$this->mockScopeDeterminator()], [3, 4]], + ['edit', TreeView::class, $treeConstructorArgs, [3, 4]], ['toggle', NonBaseView::class, [], [1, 2]], ['toggle', BaseView::class, [$this->mockScopeDeterminator()], [3, 4]], ['toggle', ListView::class, [$this->mockScopeDeterminator()], [3, 4]], ['toggle', ParentView::class, [$this->mockScopeDeterminator()], [3, 4]], - ['toggle', TreeView::class, [$this->mockScopeDeterminator()], [3, 4]], + ['toggle', TreeView::class, $treeConstructorArgs, [3, 4]], ['showAll', NonBaseView::class, [], [1, 2]], ['showAll', BaseView::class, [$this->mockScopeDeterminator()], [3, 4]], ['showAll', ListView::class, [$this->mockScopeDeterminator()], [3, 4]], ['showAll', ParentView::class, [$this->mockScopeDeterminator()], [3, 4]], - ['showAll', TreeView::class, [$this->mockScopeDeterminator()], [3, 4]], + ['showAll', TreeView::class, $treeConstructorArgs, [3, 4]], ['show', NonBaseView::class, [], [1, 2]], ['show', BaseView::class, [$this->mockScopeDeterminator()], [3, 4]], ['show', ListView::class, [$this->mockScopeDeterminator()], [3, 4]], ['show', ParentView::class, [$this->mockScopeDeterminator()], [3, 4]], - ['show', TreeView::class, [$this->mockScopeDeterminator()], [3, 4]], + ['show', TreeView::class, $treeConstructorArgs, [3, 4]], ]; } /** * @dataProvider initializePanelsDataProvider */ - public function testInitializePanels($actionName, $viewClass, $constructor, $excepted) + public function testInitializePanels(string $actionName, string $viewClass, array $constructor, array $excepted) { $dispatcher = new EventDispatcher(); @@ -723,7 +730,7 @@ private function mockScopeDeterminator() { $scopeDeterminator = $this ->getMockBuilder(RequestScopeDeterminator::class) - ->setMethods(['currentScopeIsBackend']) + ->onlyMethods(['currentScopeIsBackend']) ->disableOriginalConstructor() ->getMock(); From 313d1f76bc521789a2500b201485e64dbfc30be7 Mon Sep 17 00:00:00 2001 From: Ingolf Steinhardt Date: Mon, 11 Sep 2023 15:38:55 +0200 Subject: [PATCH 52/53] Any fixes --- .composer-require-checker.json | 8 +- .github/workflows/diagnostics.yml | 2 +- .phpcq.yaml.dist | 1 + composer.json | 2 +- src/Clipboard/ClipboardInterface.php | 2 +- src/Clipboard/Filter.php | 70 +-- .../Callback/ModelLabelCallbackListener.php | 7 +- ...rtyInputFieldGetWizardCallbackListener.php | 11 +- ...rtyInputFieldGetXLabelCallbackListener.php | 11 +- src/Contao/Compatibility/DcCompat.php | 7 +- .../Legacy/LegacyDcaDataDefinitionBuilder.php | 6 +- .../Dca/Populator/BackendViewPopulator.php | 4 + .../Dca/Populator/HardCodedPopulator.php | 13 +- src/Contao/Factory/SessionStorageFactory.php | 7 +- src/Contao/Picker/PagePickerProvider.php | 37 +- src/Contao/SessionStorage.php | 40 +- .../DynamicParentTableSubscriber.php | 14 +- .../Subscriber/FallbackResetSubscriber.php | 53 +- .../Subscriber/FormatModelLabelSubscriber.php | 31 +- .../AbstractListShowAllHandler.php | 249 ++++++--- .../ActionHandler/CopyHandler.php | 112 +++- .../ActionHandler/CreateHandler.php | 38 +- .../ActionHandler/DeleteHandler.php | 79 ++- .../ActionHandler/EditHandler.php | 48 +- .../ActionHandler/ListViewShowAllHandler.php | 20 +- .../MultipleHandler/EditAllHandler.php | 74 ++- .../MultipleHandler/OverrideAllHandler.php | 94 +++- .../MultipleHandler/PasteAllHandler.php | 108 ++-- .../MultipleHandler/SelectModelAllHandler.php | 10 +- .../SelectPropertyAllHandler.php | 110 ++-- .../ParentedListViewShowAllHandler.php | 195 +++++-- .../ActionHandler/PasteHandler.php | 44 +- .../ActionHandler/SelectHandler.php | 311 ++++++----- .../ActionHandler/ShowHandler.php | 86 ++- .../ActionHandler/ToggleHandler.php | 59 +- .../View/Contao2BackendView/BaseView.php | 212 +++++-- .../Contao2BackendView/ButtonRenderer.php | 163 ++++-- .../ContaoBackendViewTemplate.php | 70 ++- .../ContaoWidgetManager.php | 127 +++-- .../Controller/ClipboardController.php | 97 +++- .../View/Contao2BackendView/EditMask.php | 325 +++++++---- .../Event/BaseButtonEvent.php | 21 +- .../Event/BaseGetButtonsEvent.php | 9 +- .../Event/BuildWidgetEvent.php | 11 +- .../DecodePropertyValueForWidgetEvent.php | 11 +- .../EncodePropertyValueFromWidgetEvent.php | 7 +- .../Event/GetBreadcrumbEvent.php | 7 +- .../Event/GetGlobalButtonEvent.php | 11 +- .../Event/GetGroupHeaderEvent.php | 6 +- .../Event/GetOperationButtonEvent.php | 4 +- .../Event/GetParentHeaderEvent.php | 7 +- .../Event/GetPasteButtonEvent.php | 7 +- .../Event/GetPasteRootButtonEvent.php | 9 +- .../Event/ModelToLabelEvent.php | 8 +- .../Event/ParentViewChildRecordEvent.php | 2 +- .../PrepareMultipleModelsActionEvent.php | 18 +- .../EventListener/BackButtonListener.php | 19 +- .../ColorPickerWizardListener.php | 16 +- .../CreateModelButtonListener.php | 36 +- .../CreateSubHeadlineListener.php | 14 +- .../SelectModeButtonsListener.php | 18 +- .../View/Contao2BackendView/FileSelect.php | 141 +++-- .../Filter/LanguageFilter.php | 70 ++- .../GlobalButtonRenderer.php | 45 +- .../View/Contao2BackendView/PanelBuilder.php | 22 +- .../View/Contao2BackendView/PanelRenderer.php | 31 +- .../View/Contao2BackendView/ParentView.php | 11 +- .../Subscriber/CheckPermission.php | 19 +- .../Subscriber/GetGroupHeaderSubscriber.php | 46 +- .../Subscriber/MultipleHandlerSubscriber.php | 95 +++- .../Subscriber/RichTextFileUuidSubscriber.php | 18 +- .../Subscriber/WidgetBuilder.php | 128 +++-- .../View/Contao2BackendView/TreePicker.php | 515 +++++++++++++----- .../View/Contao2BackendView/TreeSelect.php | 48 +- .../View/Contao2BackendView/TreeView.php | 229 ++++++-- .../View/Contao2BackendView/ViewHelpers.php | 54 +- .../Widget/AbstractWidget.php | 14 +- .../Contao2BackendView/Widget/FileTree.php | 156 ++++-- .../Widget/FileTreeOrder.php | 13 +- .../Contao2BackendView/Widget/PageTree.php | 30 +- .../Widget/PageTreeOrder.php | 2 + .../Widget/TreePickerOrder.php | 6 +- src/ContaoManager/Plugin.php | 7 +- src/Controller/Ajax.php | 72 ++- src/Controller/Ajax3X.php | 166 ++++-- src/Controller/BackendTreeController.php | 223 +++++--- src/Controller/ControllerInterface.php | 38 +- src/Controller/DefaultController.php | 380 +++++++++---- src/Controller/ModelCollector.php | 160 ++++-- src/Controller/SortingManager.php | 107 ++-- src/Controller/TreeCollector.php | 109 ++-- src/Controller/TreeNodeStates.php | 17 +- src/DC/General.php | 48 +- src/Data/AbstractModel.php | 15 +- src/Data/ConfigInterface.php | 6 +- src/Data/DataProviderInterface.php | 4 +- src/Data/DefaultCollection.php | 21 +- src/Data/DefaultConfig.php | 87 +-- src/Data/DefaultDataProvider.php | 93 ++-- src/Data/DefaultDataProviderDBalUtils.php | 130 +++-- src/Data/DefaultDataProviderSqlUtils.php | 24 +- src/Data/DefaultEditInformation.php | 27 +- src/Data/DefaultFilterOptionCollection.php | 4 +- src/Data/DefaultLanguageInformation.php | 6 +- .../DefaultLanguageInformationCollection.php | 2 +- src/Data/DefaultModel.php | 45 +- src/Data/EditInformationInterface.php | 4 +- ...LanguageInformationCollectionInterface.php | 7 +- src/Data/LanguageInformationInterface.php | 2 +- src/Data/ModelId.php | 1 + src/Data/ModelInterface.php | 23 +- src/Data/MultiLanguageDataProvider.php | 2 + .../MultiLanguageDataProviderInterface.php | 9 +- src/Data/NoOpDataProvider.php | 7 +- src/Data/PropertyValueBag.php | 22 +- src/Data/PropertyValueBagInterface.php | 13 +- src/Data/TableRowsAsRecordsDataProvider.php | 44 +- src/DataDefinition/AbstractConditionChain.php | 2 +- ...stractEventDrivenDataDefinitionBuilder.php | 19 +- .../DataProviderInformation.php | 4 +- src/DataDefinition/DefaultContainer.php | 39 +- .../Definition/BasicDefinitionInterface.php | 2 +- .../DataProviderDefinitionInterface.php | 8 +- .../Definition/DefaultBasicDefinition.php | 6 +- .../DefaultDataProviderDefinition.php | 22 +- .../DefaultModelRelationshipDefinition.php | 9 +- .../DefaultPropertiesDefinition.php | 2 +- .../Definition/Properties/DefaultProperty.php | 48 +- .../PropertiesDefinitionInterface.php | 7 +- .../Definition/View/Command.php | 19 +- .../Definition/View/CommandCollection.php | 6 +- .../View/CommandCollectionInterface.php | 2 +- .../Definition/View/CommandInterface.php | 4 +- ...ultGroupAndSortingDefinitionCollection.php | 2 + .../DefaultGroupAndSortingInformation.php | 4 +- .../Definition/View/DefaultListingConfig.php | 8 +- .../View/DefaultModelFormatterConfig.php | 11 +- .../Definition/View/DefaultPanelRow.php | 21 +- .../View/DefaultPanelRowCollection.php | 2 +- .../View/ListingConfigInterface.php | 4 +- .../Panel/DefaultFilterElementInformation.php | 9 +- .../Panel/DefaultSearchElementInformation.php | 9 +- .../View/PanelRowCollectionInterface.php | 9 +- .../Definition/View/PanelRowInterface.php | 9 +- .../Definition/View/ToggleCommand.php | 10 +- .../View/TranslatedToggleCommand.php | 7 +- .../ModelRelationship/FilterBuilder.php | 11 +- .../FilterBuilder/AndFilterBuilder.php | 9 +- .../BaseComparingFilterBuilder.php | 18 +- .../FilterBuilder/BaseFilterBuilder.php | 51 +- .../FilterBuilderWithChildren.php | 60 +- .../FilterBuilder/OrFilterBuilder.php | 7 +- .../PropertyValueInFilterBuilder.php | 9 +- .../ParentChildCondition.php | 64 +-- .../ParentChildConditionInterface.php | 17 +- .../ModelRelationship/RootCondition.php | 15 +- .../RootConditionInterface.php | 8 +- .../Builder/Event/AddConditionEvent.php | 26 +- .../Palette/Builder/Event/BuilderEvent.php | 5 +- .../Builder/Event/CreateConditionEvent.php | 19 +- .../CreateDefaultPaletteConditionEvent.php | 21 +- .../CreatePaletteConditionChainEvent.php | 26 +- .../CreatePropertyConditionChainEvent.php | 27 +- ...tDefaultPaletteConditionClassNameEvent.php | 18 +- .../Builder/Event/SetLegendClassNameEvent.php | 13 +- .../Event/SetPaletteClassNameEvent.php | 13 +- .../SetPaletteCollectionClassNameEvent.php | 13 +- ...SetPaletteConditionChainClassNameEvent.php | 21 +- ...tePropertyValueConditionClassNameEvent.php | 18 +- .../Event/SetPropertyClassNameEvent.php | 18 +- ...etPropertyConditionChainClassNameEvent.php | 22 +- ...etPropertyValueConditionClassNameEvent.php | 18 +- .../Palette/Builder/PaletteBuilder.php | 293 +++++----- .../Palette/AbstractBoolPaletteCondition.php | 13 +- .../Palette/PaletteConditionChain.php | 10 +- .../Palette/PaletteConditionInterface.php | 11 +- .../Palette/PropertyValueCondition.php | 13 +- .../Condition/Property/BooleanCondition.php | 9 +- .../Property/DumpingPropertyCondition.php | 9 +- .../PropertyBooleanConditionTrait.php | 111 ++++ .../Property/PropertyConditionInterface.php | 21 +- .../Property/PropertyEditableCondition.php | 13 +- .../Property/PropertyFalseCondition.php | 88 +-- .../Property/PropertyTrueCondition.php | 86 +-- .../Property/PropertyValueCondition.php | 16 +- .../Property/PropertyVisibleCondition.php | 8 +- src/DataDefinition/Palette/Legend.php | 25 +- src/DataDefinition/Palette/Palette.php | 15 +- .../Palette/PaletteCollection.php | 10 +- .../Palette/PaletteCollectionInterface.php | 16 +- src/DataDefinition/Palette/Property.php | 26 +- .../Palette/PropertyInterface.php | 45 +- src/DataDefinitionContainer.php | 11 +- src/DataDefinitionContainerInterface.php | 9 +- src/DefaultEnvironment.php | 52 +- .../CcaDcGeneralExtension.php | 7 +- .../Compiler/AddSessionBagsPass.php | 7 +- src/EnvironmentInterface.php | 39 +- ...bstractEventDrivenEnvironmentPopulator.php | 8 +- .../ParentEnforcingListener.php | 18 +- .../TreeEnforcingListener.php | 33 +- src/Factory/DcGeneralFactory.php | 104 ++-- src/Factory/DcGeneralFactoryInterface.php | 29 +- src/Panel/AbstractElement.php | 42 +- src/Panel/DefaultFilterElement.php | 54 +- src/Panel/DefaultLimitElement.php | 62 ++- src/Panel/DefaultPanel.php | 12 +- src/Panel/DefaultPanelContainer.php | 33 +- src/Panel/DefaultSearchElement.php | 69 +-- src/Panel/DefaultSortElement.php | 70 +-- src/Panel/DefaultSubmitElement.php | 8 +- src/Panel/PanelContainerInterface.php | 23 +- src/Panel/PanelElementInterface.php | 21 +- src/Panel/PanelInterface.php | 29 +- src/Panel/SortElementInterface.php | 12 +- .../config/contao/picker_provider.yml | 6 +- .../contao/templates/dcbe_general_edit.html5 | 2 +- .../AbstractEnvironmentAwareHandler.php | 4 +- ...AbstractPropertyOverrideEditAllHandler.php | 64 ++- .../AbstractPropertyVisibilityHandler.php | 6 +- src/View/ViewTemplateInterface.php | 13 +- tests/Data/DefaultDataProviderTest.php | 37 +- 222 files changed, 6096 insertions(+), 3293 deletions(-) create mode 100644 src/DataDefinition/Palette/Condition/Property/PropertyBooleanConditionTrait.php diff --git a/.composer-require-checker.json b/.composer-require-checker.json index ae355952..0fb1f54b 100644 --- a/.composer-require-checker.json +++ b/.composer-require-checker.json @@ -1,11 +1,12 @@ { "symbol-whitelist": [ "array", "bool", "false", "int", "mixed", "null", "self", "static", "parent", "string", "true", "void", - "ContaoTwigInitializeEvent", + "ContaoCommunityAlliance\\Contao\\EventDispatcher\\CcaEventDispatcherBundle", "ContaoCommunityAlliance\\DcGeneral\\Contao\\View\\Contao2BackendView\\Exception\\EditOnlyModeException", "ContaoCommunityAlliance\\DcGeneral\\Contao\\View\\Contao2BackendView\\Exception\\NotCreatableException", "ContaoCommunityAlliance\\DcGeneral\\Contao\\View\\Contao2BackendView\\Exception\\NotDeletableException", - "ContaoCommunityAlliance\\Contao\\EventDispatcher\\CcaEventDispatcherBundle", + "ContaoCommunityAlliance\\DcGeneral\\DataDefinition\\Palette\\Condition\\Property\\PropertyBooleanConditionTrait", + "ContaoTwigInitializeEvent", "Contao\\Calendar", "Contao\\CalendarEventsModel", "Contao\\CalendarModel", @@ -18,6 +19,7 @@ "Contao\\NewsArchiveModel", "Contao\\NewsModel", "FOS\\HttpCache\\CacheInvalidator", - "MenAtWork\\MultiColumnWizardBundle\\Event\\GetOptionsEvent" + "MenAtWork\\MultiColumnWizardBundle\\Event\\GetOptionsEvent", + "Symfony\\Cmf\\Component\\Routing\\ChainRouterInterface" ] } diff --git a/.github/workflows/diagnostics.yml b/.github/workflows/diagnostics.yml index f6c755d9..bddb943b 100644 --- a/.github/workflows/diagnostics.yml +++ b/.github/workflows/diagnostics.yml @@ -12,7 +12,7 @@ jobs: strategy: fail-fast: false matrix: - php: [ '7.4', '8.0', '8.1', '8.2' ] + php: [ '8.1', '8.2' ] contao: [ '~4.13.0' ] phpcq_install: [ 'update' ] output: [ '-o github-action -o default' ] diff --git a/.phpcq.yaml.dist b/.phpcq.yaml.dist index ed969b08..8a8013bc 100644 --- a/.phpcq.yaml.dist +++ b/.phpcq.yaml.dist @@ -56,6 +56,7 @@ phpcq: # phpmd - 0F9684B8B16B7AB0 - A4E55EA12C7C085C + - 9093F8B32E4815AA # composer-require-checker - 033E5F8D801A2F8D diff --git a/composer.json b/composer.json index 01b6afaf..d9de59be 100644 --- a/composer.json +++ b/composer.json @@ -32,7 +32,7 @@ "source": "https://github.com/contao-community-alliance/dc-general" }, "require": { - "php": "^7.4 || ^8.0", + "php": "^8.1", "ext-json": "*", "ext-pdo": "*", "contao-community-alliance/events-contao-bindings": "^4.13", diff --git a/src/Clipboard/ClipboardInterface.php b/src/Clipboard/ClipboardInterface.php index 1e76d964..45470c93 100644 --- a/src/Clipboard/ClipboardInterface.php +++ b/src/Clipboard/ClipboardInterface.php @@ -114,7 +114,7 @@ public function hasId(ModelIdInterface $modelId); * * @param FilterInterface $filter An item filter. * - * @return ItemInterface[] + * @return list */ public function fetch(FilterInterface $filter); diff --git a/src/Clipboard/Filter.php b/src/Clipboard/Filter.php index 9a8a9acc..a3115ad0 100644 --- a/src/Clipboard/Filter.php +++ b/src/Clipboard/Filter.php @@ -22,7 +22,7 @@ namespace ContaoCommunityAlliance\DcGeneral\Clipboard; -use ContaoCommunityAlliance\DcGeneral\Data\ModelId; +use ContaoCommunityAlliance\DcGeneral\Data\ModelIdInterface; use Symfony\Component\ExpressionLanguage\ExpressionLanguage; use function count; @@ -252,11 +252,11 @@ private function modelIsNotFromProvider($conjunction, $modelProviderName) /** * And model is. * - * @param ModelId $modelId The model id. + * @param ModelIdInterface $modelId The model id. * * @return static */ - public function andModelIs(ModelId $modelId) + public function andModelIs(ModelIdInterface $modelId) { $this->modelIs('and', $modelId); @@ -266,11 +266,11 @@ public function andModelIs(ModelId $modelId) /** * Or model is. * - * @param ModelId $modelId The model id. + * @param ModelIdInterface $modelId The model id. * * @return static */ - public function orModelIs(ModelId $modelId) + public function orModelIs(ModelIdInterface $modelId) { $this->modelIs('or', $modelId); @@ -280,12 +280,12 @@ public function orModelIs(ModelId $modelId) /** * Add model is. * - * @param string $conjunction AND or OR. - * @param ModelId $modelId The model id. + * @param string $conjunction AND or OR. + * @param ModelIdInterface $modelId The model id. * * @return static */ - private function modelIs($conjunction, ModelId $modelId) + private function modelIs($conjunction, ModelIdInterface $modelId) { if (!empty($this->expression)) { $this->expression[] = $conjunction; @@ -302,11 +302,11 @@ private function modelIs($conjunction, ModelId $modelId) /** * And model is not. * - * @param ModelId $modelId The model id. + * @param ModelIdInterface $modelId The model id. * * @return static */ - public function andModelIsNot(ModelId $modelId) + public function andModelIsNot(ModelIdInterface $modelId) { $this->modelIsNot('and', $modelId); @@ -316,11 +316,11 @@ public function andModelIsNot(ModelId $modelId) /** * Or model is not. * - * @param ModelId $modelId The model id. + * @param ModelIdInterface $modelId The model id. * * @return static */ - public function orModelIsNot(ModelId $modelId) + public function orModelIsNot(ModelIdInterface $modelId) { $this->modelIsNot('or', $modelId); @@ -330,12 +330,12 @@ public function orModelIsNot(ModelId $modelId) /** * Add model is not. * - * @param string $conjunction AND or OR. - * @param ModelId $modelId The model id. + * @param string $conjunction AND or OR. + * @param ModelIdInterface $modelId The model id. * * @return static */ - private function modelIsNot($conjunction, ModelId $modelId) + private function modelIsNot($conjunction, ModelIdInterface $modelId) { if (!empty($this->expression)) { $this->expression[] = $conjunction; @@ -495,11 +495,11 @@ private function hasNoParent($conjunction) /** * And parent is. * - * @param ModelId $parentModelId The parent id. + * @param ModelIdInterface $parentModelId The parent id. * * @return static */ - public function andParentIs(ModelId $parentModelId) + public function andParentIs(ModelIdInterface $parentModelId) { $this->parentIs('and', $parentModelId); @@ -509,11 +509,11 @@ public function andParentIs(ModelId $parentModelId) /** * Or parent is. * - * @param ModelId $parentModelId The parent id. + * @param ModelIdInterface $parentModelId The parent id. * * @return static */ - public function orParentIs(ModelId $parentModelId) + public function orParentIs(ModelIdInterface $parentModelId) { $this->parentIs('or', $parentModelId); @@ -523,12 +523,12 @@ public function orParentIs(ModelId $parentModelId) /** * Add parent is. * - * @param string $conjunction AND or OR. - * @param ModelId $parentModelId The parent id. + * @param string $conjunction AND or OR. + * @param ModelIdInterface $parentModelId The parent id. * * @return static */ - private function parentIs($conjunction, ModelId $parentModelId) + private function parentIs($conjunction, ModelIdInterface $parentModelId) { if (!empty($this->expression)) { $this->expression[] = $conjunction; @@ -545,7 +545,7 @@ private function parentIs($conjunction, ModelId $parentModelId) /** * And parent is in. * - * @param array|ModelId[] $parentModelIds The parent ids. + * @param array|ModelIdInterface[] $parentModelIds The parent ids. * * @return static */ @@ -559,7 +559,7 @@ public function andParentIsIn(array $parentModelIds) /** * Or parent is in. * - * @param array|ModelId[] $parentModelIds The parent ids. + * @param array|ModelIdInterface[] $parentModelIds The parent ids. * * @return static */ @@ -574,7 +574,7 @@ public function orParentIsIn(array $parentModelIds) * Add parent is in. * * @param string $conjunction AND or OR. - * @param array|ModelId[] $parentModelIds The parent ids. + * @param array|ModelIdInterface[] $parentModelIds The parent ids. * * @return static */ @@ -599,11 +599,11 @@ private function parentIsIn($conjunction, array $parentModelIds) /** * And parent is not. * - * @param ModelId $parentModelId The parent id. + * @param ModelIdInterface $parentModelId The parent id. * * @return static */ - public function andParentIsNot(ModelId $parentModelId) + public function andParentIsNot(ModelIdInterface $parentModelId) { $this->parentIsNot('and', $parentModelId); @@ -613,11 +613,11 @@ public function andParentIsNot(ModelId $parentModelId) /** * Or parent is not. * - * @param ModelId $parentModelId The parent id. + * @param ModelIdInterface $parentModelId The parent id. * * @return static */ - public function orParentIsNot(ModelId $parentModelId) + public function orParentIsNot(ModelIdInterface $parentModelId) { $this->parentIsNot('and', $parentModelId); @@ -627,12 +627,12 @@ public function orParentIsNot(ModelId $parentModelId) /** * Add parent is not. * - * @param string $conjunction AND or OR. - * @param ModelId $parentModelId The parent id. + * @param string $conjunction AND or OR. + * @param ModelIdInterface $parentModelId The parent id. * * @return static */ - private function parentIsNot($conjunction, ModelId $parentModelId) + private function parentIsNot($conjunction, ModelIdInterface $parentModelId) { if (!empty($this->expression)) { $this->expression[] = $conjunction; @@ -649,7 +649,7 @@ private function parentIsNot($conjunction, ModelId $parentModelId) /** * And parent is not in. * - * @param array|ModelId[] $parentModelIds The parent ids. + * @param array|ModelIdInterface[] $parentModelIds The parent ids. * * @return static */ @@ -663,7 +663,7 @@ public function andParentIsNotIn(array $parentModelIds) /** * Or parent is not in. * - * @param array|ModelId[] $parentModelIds The parent ids. + * @param array|ModelIdInterface[] $parentModelIds The parent ids. * * @return static */ @@ -678,7 +678,7 @@ public function orParentIsNotIn(array $parentModelIds) * Add parent is not in. * * @param string $conjunction AND or OR. - * @param array|ModelId[] $parentModelIds The parent ids. + * @param array|ModelIdInterface[] $parentModelIds The parent ids. * * @return static */ diff --git a/src/Contao/Callback/ModelLabelCallbackListener.php b/src/Contao/Callback/ModelLabelCallbackListener.php index c4e478ba..59db1cc6 100644 --- a/src/Contao/Callback/ModelLabelCallbackListener.php +++ b/src/Contao/Callback/ModelLabelCallbackListener.php @@ -62,6 +62,7 @@ public function update($event, $value) if (!\is_array($value)) { return; } + /** @var list $value */ $this->updateTableMode($event, $value); } @@ -102,12 +103,12 @@ private function updateNonTableMode(ModelToLabelEvent $event, ?string $value): v /** * Set the value in the event. * - * @param ModelToLabelEvent $event The event being emitted. - * @param array $arguments The label arguments. + * @param ModelToLabelEvent $event The event being emitted. + * @param string|list $arguments The label arguments. * * @return void */ - private function updateTableMode(ModelToLabelEvent $event, array $arguments): void + private function updateTableMode(ModelToLabelEvent $event, array|string $arguments): void { if (empty($arguments)) { return; diff --git a/src/Contao/Callback/PropertyInputFieldGetWizardCallbackListener.php b/src/Contao/Callback/PropertyInputFieldGetWizardCallbackListener.php index 31d12da9..56d46ae5 100644 --- a/src/Contao/Callback/PropertyInputFieldGetWizardCallbackListener.php +++ b/src/Contao/Callback/PropertyInputFieldGetWizardCallbackListener.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2020 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,13 +14,15 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2020 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ namespace ContaoCommunityAlliance\DcGeneral\Contao\Callback; +use Contao\Widget; use ContaoCommunityAlliance\DcGeneral\Contao\Compatibility\DcCompat; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\BuildWidgetEvent; @@ -50,6 +52,9 @@ public function getArgs($event) */ public function update($event, $value) { - $event->getWidget()->wizard .= $value; + $widget = $event->getWidget(); + if ($widget instanceof Widget) { + $widget->wizard .= $value; + } } } diff --git a/src/Contao/Callback/PropertyInputFieldGetXLabelCallbackListener.php b/src/Contao/Callback/PropertyInputFieldGetXLabelCallbackListener.php index 5449ef86..044d1e11 100644 --- a/src/Contao/Callback/PropertyInputFieldGetXLabelCallbackListener.php +++ b/src/Contao/Callback/PropertyInputFieldGetXLabelCallbackListener.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2020 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,13 +13,15 @@ * @package contao-community-alliance/dc-general * @author Christian Schiffler * @author Sven Baumann - * @copyright 2013-2020 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ namespace ContaoCommunityAlliance\DcGeneral\Contao\Callback; +use Contao\Widget; use ContaoCommunityAlliance\DcGeneral\Contao\Compatibility\DcCompat; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\BuildWidgetEvent; @@ -49,6 +51,9 @@ public function getArgs($event) */ public function update($event, $value) { - $event->getWidget()->xlabel .= $value; + $widget = $event->getWidget(); + if ($widget instanceof Widget) { + $widget->wizard .= $value; + } } } diff --git a/src/Contao/Compatibility/DcCompat.php b/src/Contao/Compatibility/DcCompat.php index a1d08c0f..6d1fefa8 100644 --- a/src/Contao/Compatibility/DcCompat.php +++ b/src/Contao/Compatibility/DcCompat.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -30,7 +31,7 @@ use ContaoCommunityAlliance\DcGeneral\Factory\Event\PopulateEnvironmentEvent; /** - * Small compatibility layer for callbacks, that expect a "full featured" DC instance. + * Small compatibility layer for callbacks, that expect a "full-featured" DC instance. */ class DcCompat extends General { diff --git a/src/Contao/Dca/Builder/Legacy/LegacyDcaDataDefinitionBuilder.php b/src/Contao/Dca/Builder/Legacy/LegacyDcaDataDefinitionBuilder.php index fc683e59..9c35455e 100644 --- a/src/Contao/Dca/Builder/Legacy/LegacyDcaDataDefinitionBuilder.php +++ b/src/Contao/Dca/Builder/Legacy/LegacyDcaDataDefinitionBuilder.php @@ -876,8 +876,10 @@ protected function parseListSorting(ListingConfigInterface $listing, array $list $parsedProperties = []; $sortingDca = ($listDca['sorting'] ?? []); - if (isset($sortingDca['headerFields'])) { - $listing->setHeaderPropertyNames((array) $sortingDca['headerFields']); + if ($headerFields = ($sortingDca['headerFields'] ?? [])) { + assert(\is_array($headerFields)); + /** @var list $headerFields */ + $listing->setHeaderPropertyNames($headerFields); } if (isset($sortingDca['icon'])) { diff --git a/src/Contao/Dca/Populator/BackendViewPopulator.php b/src/Contao/Dca/Populator/BackendViewPopulator.php index 01a39f4e..977a29f8 100644 --- a/src/Contao/Dca/Populator/BackendViewPopulator.php +++ b/src/Contao/Dca/Populator/BackendViewPopulator.php @@ -75,6 +75,8 @@ public function __construct( ) { if (null === $tokenManager) { $tokenManager = System::getContainer()->get('security.csrf.token_manager'); + assert($tokenManager instanceof CsrfTokenManagerInterface); + // @codingStandardsIgnoreStart @trigger_error( 'Not passing the csrf token manager as 2th argument to "' . __METHOD__ . '" is deprecated ' . @@ -85,6 +87,8 @@ public function __construct( } if (null === $tokenName) { $tokenName = System::getContainer()->getParameter('contao.csrf_token_name'); + assert(\is_string($tokenName)); + // @codingStandardsIgnoreStart @trigger_error( 'Not passing the csrf token name as 3th argument to "' . __METHOD__ . '" is deprecated ' . diff --git a/src/Contao/Dca/Populator/HardCodedPopulator.php b/src/Contao/Dca/Populator/HardCodedPopulator.php index 8ce72df1..f7d4ff57 100644 --- a/src/Contao/Dca/Populator/HardCodedPopulator.php +++ b/src/Contao/Dca/Populator/HardCodedPopulator.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -71,11 +72,15 @@ public function populateController(EnvironmentInterface $environment) public function populate(EnvironmentInterface $environment) { if (!$environment->getSessionStorage()) { - /** @var SessionStorageFactory $sessionFactory */ $sessionFactory = System::getContainer()->get('cca.dc-general.session_factory'); - $definition = $environment->getDataDefinition(); + assert($sessionFactory instanceof SessionStorageFactory); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + $sessionStorage = $sessionFactory->createService(); assert($definition instanceof ContainerInterface); + $sessionStorage->setScope('DC_GENERAL_' . \strtoupper($definition->getName())); $environment->setSessionStorage($sessionStorage); // @codingStandardsIgnoreStart diff --git a/src/Contao/Factory/SessionStorageFactory.php b/src/Contao/Factory/SessionStorageFactory.php index 0247535a..bec33a78 100644 --- a/src/Contao/Factory/SessionStorageFactory.php +++ b/src/Contao/Factory/SessionStorageFactory.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -12,7 +12,8 @@ * * @package contao-community-alliance/dc-general * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -55,7 +56,7 @@ public function createService() $session = $this->container->get('session'); assert($session instanceof SessionInterface); $keys = $this->container->getParameter('cca.dc-general.session.database_keys'); - assert(is_array($keys)); + assert(\is_array($keys)); /** @var list $keys */ return new SessionStorage($session, $keys); diff --git a/src/Contao/Picker/PagePickerProvider.php b/src/Contao/Picker/PagePickerProvider.php index 52bbfe42..35a7563e 100644 --- a/src/Contao/Picker/PagePickerProvider.php +++ b/src/Contao/Picker/PagePickerProvider.php @@ -22,12 +22,46 @@ use Contao\CoreBundle\Picker\AbstractPickerProvider; use Contao\CoreBundle\Picker\DcaPickerProviderInterface; use Contao\CoreBundle\Picker\PickerConfig; +use Contao\System; +use Knp\Menu\FactoryInterface; +use Symfony\Component\Routing\RouterInterface; +use Symfony\Component\Security\Core\Security; +use Symfony\Contracts\Translation\TranslatorInterface; /** * Provides the page picker. */ class PagePickerProvider extends AbstractPickerProvider implements DcaPickerProviderInterface { + private Security $security; + + /** + * @internal + */ + public function __construct( + FactoryInterface $menuFactory, + RouterInterface $router, + ?TranslatorInterface $translator, + ?Security $security + ) { + parent::__construct($menuFactory, $router, $translator); + + if (null === $security) { + $security = System::getContainer()->get('security.helper'); + assert($security instanceof Security); + + // @codingStandardsIgnoreStart + @trigger_error( + 'Not passing the security as argument to "' . __METHOD__ . '" is deprecated ' . + 'and will cause an error in DCG 3.0', + E_USER_DEPRECATED + ); + // @codingStandardsIgnoreEnd + } + + $this->security = $security; + } + /** * {@inheritdoc} */ @@ -41,7 +75,8 @@ public function getName() */ public function supportsContext($context) { - return \in_array($context, ['cca_ page', 'cca_link'], true) && $this->getUser()->hasAccess('page', 'modules'); + return \in_array($context, ['cca_page', 'cca_link'], true) + && $this->security->isGranted('contao_user.modules', 'page'); } /** diff --git a/src/Contao/SessionStorage.php b/src/Contao/SessionStorage.php index d5354c70..b6d7956a 100644 --- a/src/Contao/SessionStorage.php +++ b/src/Contao/SessionStorage.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2022 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2022 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -22,6 +23,7 @@ namespace ContaoCommunityAlliance\DcGeneral\Contao; use ContaoCommunityAlliance\DcGeneral\SessionStorageInterface; +use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBagInterface; use Symfony\Component\HttpFoundation\Session\SessionInterface; /** @@ -32,9 +34,9 @@ class SessionStorage implements SessionStorageInterface /** * The session scope. * - * @var string + * @var null|string */ - private $scope; + private ?string $scope = null; /** * The symfony session. @@ -99,7 +101,6 @@ public function setScope($scope) // @codingStandardsIgnoreStart @\trigger_error('The scope can not be change! Use a new session storage.', E_USER_ERROR); // @codingStandardsIgnoreEnd - return; } $this->scope = $scope; @@ -175,6 +176,7 @@ public function clear() $this->load(); $this->attributes = []; $this->persist(); + return $this; } /** @@ -190,11 +192,13 @@ private function load() $sessionBag = $this->session->getBag($this->getSessionBagKey()); $databaseSessionBag = $this->session->getBag($this->getDatabaseSessionBagKey()); + assert($sessionBag instanceof AttributeBagInterface); + assert($databaseSessionBag instanceof AttributeBagInterface); - $this->attributes = \array_merge( - (array) $sessionBag->get($this->getScope()), - (array) $databaseSessionBag->get($this->getScope()) - ); + if (null === $scope = $this->getScope()) { + return; + } + $this->attributes = \array_merge((array) $sessionBag->get($scope), (array) $databaseSessionBag->get($scope)); } /** @@ -204,11 +208,17 @@ private function load() */ private function persist() { + if (null === $scope = $this->getScope()) { + return; + } + $sessionBag = $this->session->getBag($this->getSessionBagKey()); - $sessionBag->set($this->getScope(), $this->filterAttributes()); + assert($sessionBag instanceof AttributeBagInterface); + $sessionBag->set($scope, $this->filterAttributes()); $databaseSessionBag = $this->session->getBag($this->getDatabaseSessionBagKey()); - $databaseSessionBag->set($this->getScope(), $this->filterAttributes(true)); + assert($databaseSessionBag instanceof AttributeBagInterface); + $databaseSessionBag->set($scope, $this->filterAttributes(true)); } /** @@ -221,10 +231,10 @@ private function persist() */ private function filterAttributes($determineDatabase = false) { - $databaseAttributes = \array_merge( - $this->databaseKeys['common'] ?? [], - $this->databaseKeys[$this->getScope()] ?? [] - ); + $databaseAttributes = $this->databaseKeys['common'] ?? []; + if ($scope = $this->getScope()) { + $databaseAttributes = \array_merge($databaseAttributes, $this->databaseKeys[$scope] ?? []); + } if ($determineDatabase) { return \array_intersect_key($this->attributes, \array_flip($databaseAttributes)); diff --git a/src/Contao/Subscriber/DynamicParentTableSubscriber.php b/src/Contao/Subscriber/DynamicParentTableSubscriber.php index 017a3ab4..0667b7d0 100644 --- a/src/Contao/Subscriber/DynamicParentTableSubscriber.php +++ b/src/Contao/Subscriber/DynamicParentTableSubscriber.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,8 @@ * @package contao-community-alliance/dc-general * @author Christian Schiffler * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -44,7 +45,7 @@ class DynamicParentTableSubscriber implements EventSubscriberInterface * * array('eventName' => array('methodName', $priority)) * * array('eventName' => array(array('methodName1', $priority), array('methodName2')) * - * @return array The event names to listen to. + * @return array The event names to listen to. */ public static function getSubscribedEvents() { @@ -62,11 +63,12 @@ public static function getSubscribedEvents() */ public function handlePrePersistModelEvent(PrePersistModelEvent $event) { - $enviroment = $event->getEnvironment(); - $dataDefinition = $enviroment->getDataDefinition(); + $environment = $event->getEnvironment(); + $dataDefinition = $environment->getDataDefinition(); if ( - null === ($parentDataDefinition = $enviroment->getParentDataDefinition()) + null === $dataDefinition + || null === ($parentDataDefinition = $environment->getParentDataDefinition()) || (false === $dataDefinition->getPropertiesDefinition()->hasProperty('ptable')) || (false === $dataDefinition->getBasicDefinition()->isDynamicParentTable()) ) { diff --git a/src/Contao/Subscriber/FallbackResetSubscriber.php b/src/Contao/Subscriber/FallbackResetSubscriber.php index da4222c5..599c7cf5 100644 --- a/src/Contao/Subscriber/FallbackResetSubscriber.php +++ b/src/Contao/Subscriber/FallbackResetSubscriber.php @@ -24,7 +24,10 @@ use ContaoCommunityAlliance\DcGeneral\Controller\ModelCollector; use ContaoCommunityAlliance\DcGeneral\Data\ConfigInterface; +use ContaoCommunityAlliance\DcGeneral\Data\DataProviderInterface; +use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; use ContaoCommunityAlliance\DcGeneral\Data\ModelManipulator; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\Event\AbstractModelAwareEvent; use ContaoCommunityAlliance\DcGeneral\Event\PostDuplicateModelEvent; use ContaoCommunityAlliance\DcGeneral\Event\PostPersistModelEvent; @@ -51,7 +54,7 @@ class FallbackResetSubscriber implements EventSubscriberInterface * * array('eventName' => array('methodName', $priority)) * * array('eventName' => array(array('methodName1', $priority), array('methodName2')) * - * @return array The event names to listen to. + * @return array The event names to listen to. */ public static function getSubscribedEvents() { @@ -91,21 +94,32 @@ public function handlePostDuplicateModelEvent(PostDuplicateModelEvent $event) * @param AbstractModelAwareEvent $event The event. * * @return void + * + * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ private function handleFallback(AbstractModelAwareEvent $event) { - $model = $event->getModel(); + $model = $event->getModel(); + assert($model instanceof ModelInterface); + $dataProvider = $event->getEnvironment()->getDataProvider($model->getProviderName()); - $properties = $event->getEnvironment()->getDataDefinition()->getPropertiesDefinition(); + assert($dataProvider instanceof DataProviderInterface); + + $definition = $event->getEnvironment()->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $properties = $definition->getPropertiesDefinition(); foreach (\array_keys($model->getPropertiesAsArray()) as $propertyName) { if (!$properties->hasProperty($propertyName)) { continue; } + $property = $properties->getProperty($propertyName); - $extra = (array) $property->getExtra(); + $extra = $property->getExtra(); + if (\array_key_exists('fallback', $extra) && (true === $extra['fallback'])) { - // BC Layer - use old reset fallback methodology until it get's removed. + // BC Layer - use old reset fallback methodology until it gets removed. if (null === ($config = $this->determineFilterConfig($event))) { // @codingStandardsIgnoreStart @\trigger_error( @@ -114,8 +128,10 @@ private function handleFallback(AbstractModelAwareEvent $event) E_USER_DEPRECATED ); // @codingStandardsIgnoreEnd - + /** @psalm-suppress DeprecatedMethod */ $dataProvider->resetFallback($propertyName); + $dataProvider->save($model); + continue; } // If value is empty, no need to reset the fallback. @@ -126,9 +142,14 @@ private function handleFallback(AbstractModelAwareEvent $event) $models = $dataProvider->fetchAll($config); foreach ($models as $resetModel) { + if (!($resetModel instanceof ModelInterface)) { + continue; + } + if ($model->getId() === $resetModel->getId()) { continue; } + $resetModel->setProperty($propertyName, ModelManipulator::sanitizeValue($property, null)); $dataProvider->save($resetModel); } @@ -142,13 +163,23 @@ private function handleFallback(AbstractModelAwareEvent $event) * @param AbstractModelAwareEvent $event The event. * * @return ConfigInterface|null + * + * @SuppressWarnings(PHPMD.CyclomaticComplexity) + * @SuppressWarnings(NPathComplexity) */ private function determineFilterConfig(AbstractModelAwareEvent $event) { $environment = $event->getEnvironment(); $model = $event->getModel(); - $dataProvider = $environment->getDataProvider($model->getProviderName()); - $definition = $environment->getDataDefinition(); + + if (null === ($dataProvider = $environment->getDataProvider($model->getProviderName()))) { + return null; + } + + if (null === ($definition = $environment->getDataDefinition())) { + return null; + } + $relationship = $definition->getModelRelationshipDefinition(); $root = $relationship->getRootCondition(); @@ -156,8 +187,12 @@ private function determineFilterConfig(AbstractModelAwareEvent $event) return $dataProvider->getEmptyConfig()->setFilter($root->getFilterArray()); } + if (null === ($parentDefinition = $definition->getBasicDefinition()->getParentDataProvider())) { + return null; + } + $parentFilter = $relationship->getChildCondition( - $definition->getBasicDefinition()->getParentDataProvider(), + $parentDefinition, $model->getProviderName() ); diff --git a/src/Contao/Subscriber/FormatModelLabelSubscriber.php b/src/Contao/Subscriber/FormatModelLabelSubscriber.php index 449bf3a1..c08a06ee 100644 --- a/src/Contao/Subscriber/FormatModelLabelSubscriber.php +++ b/src/Contao/Subscriber/FormatModelLabelSubscriber.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2022 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -15,7 +15,7 @@ * @author Tristan Lins * @author Sven Baumann * @author Ingolf Steinhardt - * @copyright 2013-2022 Contao Community Alliance. + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -27,6 +27,7 @@ use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\ModelToLabelEvent; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\ViewHelpers; use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\PropertiesDefinitionInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\GroupAndSortingDefinitionInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\GroupAndSortingInformationInterface; @@ -57,6 +58,8 @@ public function handleFormatModelLabel(FormatModelLabelEvent $event) $model = $event->getModel(); $dataDefinition = $environment->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + /** @var Contao2BackendViewDefinitionInterface $viewSection */ $viewSection = $dataDefinition->getDefinition(Contao2BackendViewDefinitionInterface::NAME); $listing = $viewSection->getListingConfig(); @@ -72,7 +75,11 @@ public function handleFormatModelLabel(FormatModelLabelEvent $event) ->setLabel($formatter->getFormat()) ->setFormatter($formatter); - $environment->getEventDispatcher()->dispatch($modelToLabelEvent, ModelToLabelEvent::NAME); + if (null === ($dispatcher = $environment->getEventDispatcher())) { + return; + } + + $dispatcher->dispatch($modelToLabelEvent, ModelToLabelEvent::NAME); // Add columns. if ($listing->getShowColumns()) { @@ -126,7 +133,7 @@ private function getFirstSorting(GroupAndSortingDefinitionInterface $sortingDefi * @param EnvironmentInterface $environment The environment. * @param ModelInterface $model The model. * - * @return array + * @return array */ private function prepareLabelArguments( $propertyNames, @@ -155,13 +162,13 @@ private function prepareLabelArguments( /** * Render for column layout. * - * @param string[] $propertyNames The properties. - * @param string[] $args The rendered arguments. - * @param string $firstSorting The sorting column. + * @param string[] $propertyNames The properties. + * @param string|array $args The rendered arguments. + * @param string $firstSorting The sorting column. * * @return array */ - private function renderWithColumns($propertyNames, $args, $firstSorting) + private function renderWithColumns(array $propertyNames, array|string $args, string $firstSorting) { $label = []; if (!\is_array($args)) { @@ -194,13 +201,13 @@ private function renderWithColumns($propertyNames, $args, $firstSorting) /** * Render as single value. * - * @param string $label The label string. - * @param string[] $args The rendered arguments. - * @param null $maxLength The maximum length for the label or null to allow unlimited. + * @param string $label The label string. + * @param string|array $args The rendered arguments. + * @param null|int $maxLength The maximum length for the label or null to allow unlimited. * * @return string */ - private function renderSingleValue($label, $args, $maxLength = null) + private function renderSingleValue(string $label, array|string $args, ?int $maxLength = null) { // BC: sometimes the label was returned as string in the arguments instead of an array. $string = !\is_array($args) ? $args : \vsprintf($label, $args); diff --git a/src/Contao/View/Contao2BackendView/ActionHandler/AbstractListShowAllHandler.php b/src/Contao/View/Contao2BackendView/ActionHandler/AbstractListShowAllHandler.php index 5821695d..e7aacce1 100644 --- a/src/Contao/View/Contao2BackendView/ActionHandler/AbstractListShowAllHandler.php +++ b/src/Contao/View/Contao2BackendView/ActionHandler/AbstractListShowAllHandler.php @@ -32,10 +32,14 @@ use ContaoCommunityAlliance\Contao\Bindings\Events\Backend\AddToUrlEvent; use ContaoCommunityAlliance\Contao\Bindings\Events\Image\GenerateHtmlEvent; use ContaoCommunityAlliance\DcGeneral\Action; +use ContaoCommunityAlliance\DcGeneral\BaseConfigRegistry; +use ContaoCommunityAlliance\DcGeneral\Clipboard\ClipboardInterface; use ContaoCommunityAlliance\DcGeneral\Clipboard\Filter; +use ContaoCommunityAlliance\DcGeneral\Clipboard\FilterInterface; use ContaoCommunityAlliance\DcGeneral\Contao\DataDefinition\Definition\Contao2BackendViewDefinitionInterface; use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminator; use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminatorAwareTrait; +use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\BackendViewInterface; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\ButtonRenderer; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\ContaoBackendViewTemplate; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\GetBreadcrumbEvent; @@ -44,12 +48,16 @@ use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\GlobalButtonRenderer; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\PanelRenderer; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\ViewHelpers; +use ContaoCommunityAlliance\DcGeneral\Controller\ControllerInterface; use ContaoCommunityAlliance\DcGeneral\Data\CollectionInterface; +use ContaoCommunityAlliance\DcGeneral\Data\DataProviderInterface; use ContaoCommunityAlliance\DcGeneral\Data\ModelId; use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; use ContaoCommunityAlliance\DcGeneral\Data\MultiLanguageDataProviderInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\BasicDefinitionInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\DefinitionInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\GroupAndSortingDefinitionCollectionInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\GroupAndSortingDefinitionInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\GroupAndSortingInformationInterface; use ContaoCommunityAlliance\DcGeneral\DcGeneralEvents; @@ -58,15 +66,17 @@ use ContaoCommunityAlliance\DcGeneral\Event\ActionEvent; use ContaoCommunityAlliance\DcGeneral\Event\FormatModelLabelEvent; use ContaoCommunityAlliance\DcGeneral\Event\ViewEvent; +use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; +use ContaoCommunityAlliance\DcGeneral\Panel\PanelContainerInterface; +use ContaoCommunityAlliance\DcGeneral\SessionStorageInterface; use ContaoCommunityAlliance\DcGeneral\View\ActionHandler\CallActionTrait; use ContaoCommunityAlliance\Translator\TranslatorInterface as CcaTranslator; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface; use Symfony\Contracts\Translation\TranslatorInterface; use function array_key_exists; use function implode; -use function in_array; -use function sprintf; use function str_replace; use function strpos; use function trigger_error; @@ -92,9 +102,9 @@ abstract class AbstractListShowAllHandler /** * The cca translator. * - * @var CcaTranslator|TranslatorInterface + * @var CcaTranslator */ - private $ccaTranslator; + private CcaTranslator $ccaTranslator; /** * The token manager. @@ -113,11 +123,11 @@ abstract class AbstractListShowAllHandler /** * AbstractHandler constructor. * - * @param RequestScopeDeterminator $scopeDeterminator The request mode determinator. - * @param TranslatorInterface $translator The translator. - * @param CcaTranslator|TranslatorInterface $ccaTranslator The cca translator. - * @param CsrfTokenManagerInterface|null $tokenManager The token manager. - * @param string|null $tokenName The token name. + * @param RequestScopeDeterminator $scopeDeterminator The request mode determinator. + * @param TranslatorInterface $translator The translator. + * @param CcaTranslator $ccaTranslator The cca translator. + * @param CsrfTokenManagerInterface|null $tokenManager The token manager. + * @param string|null $tokenName The token name. */ public function __construct( RequestScopeDeterminator $scopeDeterminator, @@ -133,6 +143,8 @@ public function __construct( if (null === $tokenManager) { $tokenManager = System::getContainer()->get('security.csrf.token_manager'); + assert($tokenManager instanceof CsrfTokenManagerInterface); + // @codingStandardsIgnoreStart @trigger_error( 'Not passing the csrf token manager as 4th argument to "' . __METHOD__ . '" is deprecated ' . @@ -143,6 +155,8 @@ public function __construct( } if (null === $tokenName) { $tokenName = System::getContainer()->getParameter('contao.csrf_token_name'); + assert(\is_string($tokenName)); + // @codingStandardsIgnoreStart @trigger_error( 'Not passing the csrf token name as 5th argument to "' . __METHOD__ . '" is deprecated ' . @@ -170,18 +184,23 @@ public function handleEvent(ActionEvent $event) } $environment = $event->getEnvironment(); - $basic = $environment->getDataDefinition()->getBasicDefinition(); - $action = $event->getAction(); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $basic = $definition->getBasicDefinition(); + $action = $event->getAction(); if ( - null !== $event->getResponse() + (null === $mode = $basic->getMode()) + || null !== $event->getResponse() || ('showAll' !== $action->getName()) - || !$this->wantToHandle($basic->getMode(), $action) + || !$this->wantToHandle($mode, $action) ) { return; } - if (false !== ($response = $this->process($action, $environment))) { + if (null !== ($response = $this->process($action, $environment))) { $event->setResponse($response); } } @@ -192,12 +211,15 @@ public function handleEvent(ActionEvent $event) * @param Action $action The action being handled. * @param EnvironmentInterface $environment Current dc-general environment. * - * @return string + * @return string|null */ protected function process(Action $action, EnvironmentInterface $environment) { + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + // Edit only mode, forward to edit action. - if ($environment->getDataDefinition()->getBasicDefinition()->isEditOnlyMode()) { + if ($definition->getBasicDefinition()->isEditOnlyMode()) { return $this->callAction($environment, 'edit', $action->getArguments()); } @@ -207,17 +229,22 @@ protected function process(Action $action, EnvironmentInterface $environment) // Process now. $collection = $this->loadCollection($environment); + assert($collection instanceof CollectionInterface); $this->handleEditAllButton($collection, $environment); - $this->renderCollection($environment, $collection, $grouping); + $this->renderCollection($environment, $collection, $grouping ?: []); - $template = $this->determineTemplate($grouping) + $template = $this->determineTemplate($grouping ?: []); + $template ->set('collection', $collection) ->set('mode', ($grouping ? $grouping['mode'] : null)) ->set('theme', Backend::getTheme()); $this->renderTemplate($template, $environment); + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + $clipboard = new ViewEvent($environment, $action, DcGeneralViews::CLIPBOARD, []); - $environment->getEventDispatcher()->dispatch($clipboard, DcGeneralEvents::VIEW); + $dispatcher->dispatch($clipboard, DcGeneralEvents::VIEW); return implode( "\n", @@ -232,7 +259,7 @@ protected function process(Action $action, EnvironmentInterface $environment) } /** - * Execute the multi language support. + * Execute the multi-language support. * * @param EnvironmentInterface $environment The environment. * @@ -247,10 +274,12 @@ private function languageSwitcher(EnvironmentInterface $environment) return ''; } - /** @var MultiLanguageDataProviderInterface $dataProvider */ + $controller = $environment->getController(); + assert($controller instanceof ControllerInterface); + /** @var MultiLanguageDataProviderInterface $dataProvider */ return $template - ->set('languages', $environment->getController()->getSupportedLanguages(null)) + ->set('languages', $controller->getSupportedLanguages(null)) ->set('language', $dataProvider->getCurrentLanguage()) ->set('submit', $this->translator->trans('MSC.showSelected', [], 'contao_default')) ->set('REQUEST_TOKEN', $this->tokenManager->getToken($this->tokenName)) @@ -262,20 +291,22 @@ private function languageSwitcher(EnvironmentInterface $environment) * * @param ContainerInterface $definition Data container definition. * - * @return DefinitionInterface|Contao2BackendViewDefinitionInterface + * @return Contao2BackendViewDefinitionInterface */ protected function getViewSection(ContainerInterface $definition) { - return $definition->getDefinition(Contao2BackendViewDefinitionInterface::NAME); + $viewDefinition = $definition->getDefinition(Contao2BackendViewDefinitionInterface::NAME); + assert($viewDefinition instanceof Contao2BackendViewDefinitionInterface); + return $viewDefinition; } /** * Check if the action should be handled. * - * @param string $mode The list mode. + * @param int $mode The list mode. * @param Action $action The action. * - * @return mixed + * @return bool */ abstract protected function wantToHandle($mode, Action $action); @@ -286,7 +317,7 @@ abstract protected function wantToHandle($mode, Action $action); * @param string|null $domain The domain name to use. * @param array $parameters Parameters. * - * @return array|string + * @return string */ protected function translate($key, $domain, array $parameters = []) { @@ -303,7 +334,11 @@ protected function translate($key, $domain, array $parameters = []) // @codingStandardsIgnoreEnd $translated = - $this->translator->trans(sprintf('%s.%s', $domain, $key), $parameters, sprintf('contao_%s', $domain)); + $this->translator->trans( + \sprintf('%s.%s', $domain ?? '', $key), + $parameters, + \sprintf('contao_%s', $domain ?? '') + ); } return $translated; @@ -319,8 +354,11 @@ protected function translate($key, $domain, array $parameters = []) */ protected function renderModel(ModelInterface $model, EnvironmentInterface $environment) { + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + $event = new FormatModelLabelEvent($environment, $model); - $environment->getEventDispatcher()->dispatch($event, DcGeneralEvents::FORMAT_MODEL_LABEL); + $dispatcher->dispatch($event, DcGeneralEvents::FORMAT_MODEL_LABEL); $model->setMeta($model::LABEL_VALUE, $event->getLabel()); } @@ -357,12 +395,17 @@ abstract protected function determineTemplate($groupingInformation); protected function renderTemplate(ContaoBackendViewTemplate $template, EnvironmentInterface $environment) { $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $provider = $environment->getInputProvider(); + assert($provider instanceof InputProviderInterface); + $showColumn = $this->getViewSection($definition)->getListingConfig()->getShowColumns(); $template ->set('subHeadline', $this->translate('MSC.select_models', 'contao_default')) - ->set('tableName', ($definition->getName() ?? 'none')) - ->set('select', 'select' === $environment->getInputProvider()->getParameter('act')) + ->set('tableName', ($definition->getName() ?: 'none')) + ->set('select', 'select' === $provider->getParameter('act')) ->set('action', StringUtil::ampersand(Environment::get('request'))) ->set('selectButtons', $this->getSelectButtons($environment)) ->set('sortable', $this->isSortable($environment)) @@ -390,17 +433,30 @@ protected function renderTemplate(ContaoBackendViewTemplate $template, Environme * * @param EnvironmentInterface $environment The environment. * - * @return CollectionInterface + * @return CollectionInterface|list */ protected function loadCollection(EnvironmentInterface $environment) { - $dataConfig = $environment->getBaseConfigRegistry()->getBaseConfig(); - $listingConfig = $this->getViewSection($environment->getDataDefinition())->getListingConfig(); - $panel = $environment->getView()->getPanel(); + $config = $environment->getBaseConfigRegistry(); + assert($config instanceof BaseConfigRegistry); + + $view = $environment->getView(); + assert($view instanceof BackendViewInterface); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $dataConfig = $config->getBaseConfig(); + $listingConfig = $this->getViewSection($definition)->getListingConfig(); + $panel = $view->getPanel(); + assert($panel instanceof PanelContainerInterface); ViewHelpers::initializeSorting($panel, $dataConfig, $listingConfig); - return $environment->getDataProvider()->fetchAll($dataConfig); + $provider = $environment->getDataProvider(); + assert($provider instanceof DataProviderInterface); + + return $provider->fetchAll($dataConfig); } /** @@ -424,15 +480,25 @@ private function generateHeaderButtons(EnvironmentInterface $environment) * * @return void */ - private function renderCollection(EnvironmentInterface $environment, CollectionInterface $collection, $grouping) - { - $listing = $this->getViewSection($environment->getDataDefinition())->getListingConfig(); + private function renderCollection( + EnvironmentInterface $environment, + CollectionInterface $collection, + array $grouping + ) { + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $listing = $this->getViewSection($definition)->getListingConfig(); $remoteCur = null; $groupClass = 'tl_folder_tlist'; $eoCount = -1; + if (null === ($inputProvider = $environment->getInputProvider())) { + return; + } + // Generate buttons - only if not in select mode! - if ('select' !== $environment->getInputProvider()->getParameter('act')) { + if ('select' !== $inputProvider->getParameter('act')) { $buttonRenderer = new ButtonRenderer($environment); $buttonRenderer->renderButtonsForCollection($collection); } @@ -440,7 +506,7 @@ private function renderCollection(EnvironmentInterface $environment, CollectionI // Run each model. foreach ($collection as $model) { /** @var ModelInterface $model */ - $this->addGroupHeader($environment, (array) $grouping, $model, $groupClass, $eoCount, $remoteCur); + $this->addGroupHeader($environment, $grouping, $model, $groupClass, $eoCount, $remoteCur); if ($listing->getItemCssClass()) { $model->setMeta($model::CSS_CLASS, $listing->getItemCssClass()); @@ -451,7 +517,11 @@ private function renderCollection(EnvironmentInterface $environment, CollectionI $cssClasses[] = $model->getMeta($model::CSS_ROW_CLASS) : null; $modelId = ModelId::fromModel($model); - if ($environment->getClipboard()->hasId($modelId)) { + + if ( + null !== ($clipboard = $environment->getClipboard()) + && $clipboard->hasId($modelId) + ) { $cssClasses[] = 'tl_folder_clipped'; } @@ -516,7 +586,10 @@ private function addGroupHeader( */ private function panel(EnvironmentInterface $environment, $ignoredPanels = []) { - return (new PanelRenderer($environment->getView()))->render($ignoredPanels); + $view = $environment->getView(); + assert($view instanceof BackendViewInterface); + + return (new PanelRenderer($view))->render($ignoredPanels); } /** @@ -528,15 +601,17 @@ private function panel(EnvironmentInterface $environment, $ignoredPanels = []) */ private function getTableHead(EnvironmentInterface $environment) { - $tableHead = []; $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $tableHead = []; $properties = $definition->getPropertiesDefinition(); $formatter = $this->getViewSection($definition)->getListingConfig()->getLabelFormatter($definition->getName()); $sorting = ViewHelpers::getCurrentSorting($environment); $columns = $this->getSortingColumns($sorting); foreach ($formatter->getPropertyNames() as $field) { $tableHead[] = [ - 'class' => 'tl_folder_tlist col_' . $field . (in_array($field, $columns) ? ' ordered_by' : ''), + 'class' => 'tl_folder_tlist col_' . $field . (\in_array($field, $columns) ? ' ordered_by' : ''), 'content' => $properties->hasProperty($field) ? $properties->getProperty($field)->getLabel() : $this->translate($definition->getName() . '.' . $field . '.0', 'contao_' . $definition->getName()) @@ -560,7 +635,7 @@ private function getTableHead(EnvironmentInterface $environment) * @param int $groupLength The length of the value to use for grouping (only used when grouping. * @param EnvironmentInterface $environment The environment. * - * @return string + * @return string|null * * @SuppressWarnings(PHPMD.Superglobals) * @SuppressWarnings(PHPMD.CamelCaseVariableName) @@ -572,13 +647,19 @@ private function renderGroupHeader( $groupLength, EnvironmentInterface $environment ) { + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + // No property? Get out! - if (!$environment->getDataDefinition()->getPropertiesDefinition()->getProperty($field)) { + if (!$definition->getPropertiesDefinition()->hasProperty($field)) { return '-'; } + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + $event = new GetGroupHeaderEvent($environment, $model, $field, null, $groupMode, $groupLength); - $environment->getEventDispatcher()->dispatch($event, $event::NAME); + $dispatcher->dispatch($event, $event::NAME); return $event->getValue(); } @@ -594,7 +675,11 @@ protected function getSelectButtons(EnvironmentInterface $environment) { $event = new GetSelectModeButtonsEvent($environment); $event->setButtons([]); - $environment->getEventDispatcher()->dispatch($event, GetSelectModeButtonsEvent::NAME); + + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $dispatcher->dispatch($event, GetSelectModeButtonsEvent::NAME); return $event->getButtons(); } @@ -608,32 +693,54 @@ protected function getSelectButtons(EnvironmentInterface $environment) */ private function isSortable(EnvironmentInterface $environment) { + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + return ((true === (bool) ViewHelpers::getManualSortingProperty($environment)) - && (true === $environment->getDataDefinition()->getBasicDefinition()->isEditable())); + && (true === $definition->getBasicDefinition()->isEditable())); } /** * Render paste top button. Returns null if no button should be rendered. * - * @param EnvironmentInterface $environment The environment. - * @param GroupAndSortingDefinitionInterface|null $sorting The sorting mode. + * @param EnvironmentInterface $environment The + * env. + * @param GroupAndSortingDefinitionInterface|GroupAndSortingDefinitionCollectionInterface|null $sorting The + * sorting + * mode. * * @return string */ protected function renderPasteTopButton(EnvironmentInterface $environment, $sorting) { - $definition = $environment->getDataDefinition(); - $dispatcher = $environment->getEventDispatcher(); + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + $languageDomain = 'contao_' . $definition->getName(); $filter = new Filter(); - $filter->andModelIsFromProvider($definition->getBasicDefinition()->getDataProvider()); + assert($filter instanceof FilterInterface); - if (!$sorting || $environment->getClipboard()->isEmpty($filter)) { - return null; + $basicDefinition = $definition->getBasicDefinition(); + assert($basicDefinition instanceof BasicDefinitionInterface); + + $dataProvider = $basicDefinition->getDataProvider(); + assert(\is_string($dataProvider)); + + $filter->andModelIsFromProvider($dataProvider); + + $clipboard = $environment->getClipboard(); + assert($clipboard instanceof ClipboardInterface); + + if (!$sorting || $clipboard->isEmpty($filter)) { + return ''; } + if (!ViewHelpers::getManualSortingProperty($environment)) { - return null; + return ''; } /** @var AddToUrlEvent $urlEvent */ @@ -654,11 +761,11 @@ protected function renderPasteTopButton(EnvironmentInterface $environment, $sort ContaoEvents::IMAGE_GET_HTML ); - return sprintf( + return \sprintf( '%s', $urlEvent->getUrl(), StringUtil::specialchars($this->translate('pasteafter.0', $languageDomain)), - $imageEvent->getHtml() + $imageEvent->getHtml() ?? '' ); } @@ -675,7 +782,11 @@ protected function renderPasteTopButton(EnvironmentInterface $environment, $sort private function breadcrumb(EnvironmentInterface $environment) { $event = new GetBreadcrumbEvent($environment); - $environment->getEventDispatcher()->dispatch($event, $event::NAME); + + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $dispatcher->dispatch($event, $event::NAME); $elements = $event->getElements(); if (empty($elements)) { return null; @@ -723,10 +834,16 @@ private function getSortingColumns($sortingDefinition) */ private function getSelectContainer(EnvironmentInterface $environment) { - $inputProvider = $environment->getInputProvider(); + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + $sessionStorage = $environment->getSessionStorage(); + assert($sessionStorage instanceof SessionStorageInterface); - $sessionName = $environment->getDataDefinition()->getName() . '.' . $inputProvider->getParameter('mode'); + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $sessionName = $definition->getName() . '.' . $inputProvider->getParameter('mode'); if (!$sessionStorage->has($sessionName)) { return []; } @@ -759,7 +876,11 @@ private function handleEditAllButton(CollectionInterface $collection, Environmen } $dataDefinition = $environment->getDataDefinition(); - $backendView = $dataDefinition->getDefinition(Contao2BackendViewDefinitionInterface::NAME); + assert($dataDefinition instanceof ContainerInterface); + + $backendView = $dataDefinition->getDefinition(Contao2BackendViewDefinitionInterface::NAME); + assert($backendView instanceof Contao2BackendViewDefinitionInterface); + $globalCommands = $backendView->getGlobalCommands(); if (!$globalCommands->hasCommandNamed('all')) { diff --git a/src/Contao/View/Contao2BackendView/ActionHandler/CopyHandler.php b/src/Contao/View/Contao2BackendView/ActionHandler/CopyHandler.php index 6cec1b9d..4ff8475e 100644 --- a/src/Contao/View/Contao2BackendView/ActionHandler/CopyHandler.php +++ b/src/Contao/View/Contao2BackendView/ActionHandler/CopyHandler.php @@ -32,17 +32,22 @@ use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminatorAwareTrait; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Exception\NotCreatableException; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\ViewHelpers; +use ContaoCommunityAlliance\DcGeneral\Controller\ControllerInterface; +use ContaoCommunityAlliance\DcGeneral\Data\DataProviderInterface; use ContaoCommunityAlliance\DcGeneral\Data\ModelId; use ContaoCommunityAlliance\DcGeneral\Data\ModelIdInterface; use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; use ContaoCommunityAlliance\DcGeneral\Event\ActionEvent; use ContaoCommunityAlliance\DcGeneral\Event\PostDuplicateModelEvent; use ContaoCommunityAlliance\DcGeneral\Event\PreDuplicateModelEvent; +use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralRuntimeException; use ContaoCommunityAlliance\DcGeneral\View\ActionHandler\ActionGuardTrait; use ContaoCommunityAlliance\DcGeneral\View\ActionHandler\CallActionTrait; use ContaoCommunityAlliance\UrlBuilder\Contao\CsrfUrlBuilderFactory; use ContaoCommunityAlliance\UrlBuilder\UrlBuilder; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; /** * Class CopyModelController handles copy action on a model. @@ -89,7 +94,12 @@ public function handleEvent(ActionEvent $event) } $environment = $event->getEnvironment(); - if (!$environment->getDataDefinition()->getBasicDefinition()->isCreatable()) { + + if (null === ($definition = $environment->getDataDefinition())) { + return; + } + + if (!$definition->getBasicDefinition()->isCreatable()) { return; } @@ -98,7 +108,7 @@ public function handleEvent(ActionEvent $event) } if (true !== ($response = $this->checkPermission($environment))) { - $event->setResponse($response); + $event->setResponse((string) $response); $event->stopPropagation(); return; @@ -123,12 +133,15 @@ public function handleEvent(ActionEvent $event) protected function guardIsCreatable(EnvironmentInterface $environment, ModelIdInterface $modelId, $redirect = false) { $dataDefinition = $environment->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + if ($dataDefinition->getBasicDefinition()->isCreatable()) { return; } if ($redirect) { $eventDispatcher = $environment->getEventDispatcher(); + assert($eventDispatcher instanceof EventDispatcherInterface); $eventDispatcher->dispatch( new LogEvent( @@ -161,26 +174,44 @@ protected function guardIsCreatable(EnvironmentInterface $environment, ModelIdIn */ public function copy(EnvironmentInterface $environment, ModelIdInterface $modelId) { - $this->guardNotEditOnly($environment->getDataDefinition(), $modelId); + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $this->guardNotEditOnly($definition, $modelId); $this->guardIsCreatable($environment, $modelId); $dataProvider = $environment->getDataProvider(); - $model = $dataProvider->fetch($dataProvider->getEmptyConfig()->setId($modelId->getId())); + assert($dataProvider instanceof DataProviderInterface); + $model = $dataProvider->fetch($dataProvider->getEmptyConfig()->setId($modelId->getId())); + + if (!$model) { + throw new DcGeneralRuntimeException( + 'Model not found with ID ' . $modelId->getId() + ); + } + $controller = $environment->getController(); + assert($controller instanceof ControllerInterface); // We need to keep the original data here. - $copyModel = $environment->getController()->createClonedModel($model); + $copyModel = $controller->createClonedModel($model); - $eventDispatcher = $environment->getEventDispatcher(); - // Dispatch pre duplicate event. - $preCopyEvent = new PreDuplicateModelEvent($environment, $copyModel, $model); - $eventDispatcher->dispatch($preCopyEvent, $preCopyEvent::NAME); + $dispatcher = $environment->getEventDispatcher(); + if (null !== $dispatcher) { + // Dispatch pre duplicate event. + $preCopyEvent = new PreDuplicateModelEvent($environment, $copyModel, $model); + $dispatcher->dispatch($preCopyEvent, $preCopyEvent::NAME); + } // Save the copy. - $environment->getDataProvider($copyModel->getProviderName())->save($copyModel); - - // Dispatch post duplicate event. - $postCopyEvent = new PostDuplicateModelEvent($environment, $copyModel, $model); - $eventDispatcher->dispatch($postCopyEvent, $postCopyEvent::NAME); + $dataProvider = $environment->getDataProvider($copyModel->getProviderName()); + assert($dataProvider instanceof DataProviderInterface); + $dataProvider->save($copyModel); + + if (null !== $dispatcher) { + // Dispatch post duplicate event. + $postCopyEvent = new PostDuplicateModelEvent($environment, $copyModel, $model); + $dispatcher->dispatch($postCopyEvent, $postCopyEvent::NAME); + } return $copyModel; } @@ -195,18 +226,27 @@ public function copy(EnvironmentInterface $environment, ModelIdInterface $modelI */ protected function redirect($environment, $copiedModelId) { + if (null === ($inputProvider = $environment->getInputProvider())) { + return; + } + // Build a clean url to remove the copy related arguments instead of using the AddToUrlEvent. $urlBuilder = new UrlBuilder(); $urlBuilder ->setPath('contao') - ->setQueryParameter('do', $environment->getInputProvider()->getParameter('do')) + ->setQueryParameter('do', $inputProvider->getParameter('do')) ->setQueryParameter('table', $copiedModelId->getDataProviderName()) ->setQueryParameter('act', 'edit') ->setQueryParameter('id', $copiedModelId->getSerialized()) - ->setQueryParameter('pid', $environment->getInputProvider()->getParameter('pid')); + ->setQueryParameter('pid', $inputProvider->getParameter('pid')); $redirectEvent = new RedirectEvent($this->securityUrlBuilder->create($urlBuilder->getUrl())->getUrl()); - $environment->getEventDispatcher()->dispatch($redirectEvent, ContaoEvents::CONTROLLER_REDIRECT); + + if (null === ($dispatcher = $environment->getEventDispatcher())) { + return; + } + + $dispatcher->dispatch($redirectEvent, ContaoEvents::CONTROLLER_REDIRECT); } /** @@ -214,31 +254,43 @@ protected function redirect($environment, $copiedModelId) * * @param EnvironmentInterface $environment Current dc-general environment. * - * @return string|bool|null + * @return string|false|null */ protected function process(EnvironmentInterface $environment) { - $modelId = ModelId::fromSerialized($environment->getInputProvider()->getParameter('source')); + if (null === ($inputProvider = $environment->getInputProvider())) { + return false; + } - $dataDefinition = $environment->getDataDefinition(); - $this->guardValidEnvironment($dataDefinition, $modelId); + $modelId = ModelId::fromSerialized($inputProvider->getParameter('source')); + + if (null === ($definition = $environment->getDataDefinition())) { + return false; + } + + $this->guardValidEnvironment($definition, $modelId); // We want a redirect here if not creatable. $this->guardIsCreatable($environment, $modelId, true); - if ($dataDefinition->getBasicDefinition()->isEditOnlyMode()) { + if ($definition->getBasicDefinition()->isEditOnlyMode()) { return $this->callAction($environment, 'edit'); } // Manual sorting mode. The ClipboardController should pick it up. $manualSorting = ViewHelpers::getManualSortingProperty($environment); - if ($manualSorting && $environment->getDataProvider()->fieldExists($manualSorting)) { + + if (null === ($provider = $environment->getDataProvider())) { + return false; + } + + if ($manualSorting && $provider->fieldExists($manualSorting)) { return false; } $copiedModel = $this->copy($environment, $modelId); // If edit several don´t redirect do home. - if ('select' === $environment->getInputProvider()->getParameter('act')) { + if ('select' === $inputProvider->getParameter('act')) { return false; } @@ -256,15 +308,23 @@ protected function process(EnvironmentInterface $environment) */ private function checkPermission(EnvironmentInterface $environment) { - if (true === $environment->getDataDefinition()->getBasicDefinition()->isCreatable()) { + if (null === ($definition = $environment->getDataDefinition())) { + return false; + } + + if (true === $definition->getBasicDefinition()->isCreatable()) { return true; } + if (null === ($inputProvider = $environment->getInputProvider())) { + return ''; + } + return \sprintf( '
You have no permission for copy model %s.
', - ModelId::fromSerialized($environment->getInputProvider()->getParameter('source'))->getSerialized() + ModelId::fromSerialized($inputProvider->getParameter('source'))->getSerialized() ); } } diff --git a/src/Contao/View/Contao2BackendView/ActionHandler/CreateHandler.php b/src/Contao/View/Contao2BackendView/ActionHandler/CreateHandler.php index f5c07f22..fcbef4e8 100644 --- a/src/Contao/View/Contao2BackendView/ActionHandler/CreateHandler.php +++ b/src/Contao/View/Contao2BackendView/ActionHandler/CreateHandler.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2022 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -17,7 +17,7 @@ * @author Sven Baumann * @author Richard Henkenjohann * @author Ingolf Steinhardt - * @copyright 2013-2022 Contao Community Alliance. + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -30,10 +30,13 @@ use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\BaseView; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\EditMask; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\ViewHelpers; +use ContaoCommunityAlliance\DcGeneral\Data\DataProviderInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\BackCommand; use ContaoCommunityAlliance\DcGeneral\Data\DefaultEditInformation; use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; use ContaoCommunityAlliance\DcGeneral\Event\ActionEvent; +use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; /** * Class CreateHandler @@ -82,8 +85,9 @@ public function handleEvent(ActionEvent $event) $environment = $event->getEnvironment(); $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); - // Only handle if we do not have a manual sorting or we know where to insert. + // Only handle if we do not have a manual sorting, or we know where to insert. // Manual sorting is handled by clipboard. if ( ViewHelpers::getManualSortingProperty($environment) @@ -94,13 +98,13 @@ public function handleEvent(ActionEvent $event) } if (true !== ($response = $this->checkPermission($environment))) { - $event->setResponse($response); + $event->setResponse((string) $response); $event->stopPropagation(); return; } - if (false !== ($response = $this->process($environment))) { + if ('' !== ($response = $this->process($environment))) { $event->setResponse($response); } } @@ -115,9 +119,13 @@ public function handleEvent(ActionEvent $event) protected function process(EnvironmentInterface $environment) { $dataProvider = $environment->getDataProvider(); - $properties = $environment->getDataDefinition()->getPropertiesDefinition()->getProperties(); - $model = $dataProvider->getEmptyModel(); - $clone = $dataProvider->getEmptyModel(); + assert($dataProvider instanceof DataProviderInterface); + $dataDefinition = $environment->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + + $properties = $dataDefinition->getPropertiesDefinition()->getProperties(); + $model = $dataProvider->getEmptyModel(); + $clone = $dataProvider->getEmptyModel(); // If some of the fields have a default value, set it. foreach ($properties as $property) { @@ -133,10 +141,13 @@ protected function process(EnvironmentInterface $environment) $view = $environment->getView(); if (!$view instanceof BaseView) { - return false; + return ''; } - if ('select' !== $environment->getInputProvider()->getParameter('act')) { + $provider = $environment->getInputProvider(); + assert($provider instanceof InputProviderInterface); + + if ('select' !== $provider->getParameter('act')) { $this->handleGlobalCommands($environment); } @@ -154,6 +165,7 @@ protected function process(EnvironmentInterface $environment) private function checkPermission(EnvironmentInterface $environment) { $dataDefinition = $environment->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); if (true === $dataDefinition->getBasicDefinition()->isCreatable()) { return true; @@ -177,7 +189,11 @@ private function checkPermission(EnvironmentInterface $environment) protected function handleGlobalCommands(EnvironmentInterface $environment) { $dataDefinition = $environment->getDataDefinition(); - $backendView = $dataDefinition->getDefinition(Contao2BackendViewDefinitionInterface::NAME); + assert($dataDefinition instanceof ContainerInterface); + + $backendView = $dataDefinition->getDefinition(Contao2BackendViewDefinitionInterface::NAME); + assert($backendView instanceof Contao2BackendViewDefinitionInterface); + $globalCommands = $backendView->getGlobalCommands(); $globalCommands->clearCommands(); diff --git a/src/Contao/View/Contao2BackendView/ActionHandler/DeleteHandler.php b/src/Contao/View/Contao2BackendView/ActionHandler/DeleteHandler.php index f2e74dee..1e9c5698 100644 --- a/src/Contao/View/Contao2BackendView/ActionHandler/DeleteHandler.php +++ b/src/Contao/View/Contao2BackendView/ActionHandler/DeleteHandler.php @@ -31,10 +31,12 @@ use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Exception\EditOnlyModeException; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Exception\NotDeletableException; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\ViewHelpers; +use ContaoCommunityAlliance\DcGeneral\Data\DataProviderInterface; use ContaoCommunityAlliance\DcGeneral\Data\DefaultCollection; use ContaoCommunityAlliance\DcGeneral\Data\ModelId; use ContaoCommunityAlliance\DcGeneral\Data\ModelIdInterface; use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\DefaultModelRelationshipDefinition; use ContaoCommunityAlliance\DcGeneral\DataDefinition\ModelRelationship\ParentChildConditionInterface; use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; @@ -42,8 +44,10 @@ use ContaoCommunityAlliance\DcGeneral\Event\PostDeleteModelEvent; use ContaoCommunityAlliance\DcGeneral\Event\PreDeleteModelEvent; use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralRuntimeException; +use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; use ContaoCommunityAlliance\DcGeneral\View\ActionHandler\ActionGuardTrait; use ContaoCommunityAlliance\DcGeneral\View\ActionHandler\CallActionTrait; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; /** * Class DeleteHandler handles the delete action. @@ -84,7 +88,7 @@ public function handleEvent(ActionEvent $event) } if (true !== ($response = $this->checkPermission($event->getEnvironment()))) { - $event->setResponse($response); + $event->setResponse((string) $response); $event->stopPropagation(); return; @@ -108,6 +112,8 @@ public function handleEvent(ActionEvent $event) protected function guardIsDeletable(EnvironmentInterface $environment, ModelIdInterface $modelId, $redirect = false) { $dataDefinition = $environment->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + if ($dataDefinition->getBasicDefinition()->isDeletable()) { return; } @@ -117,6 +123,7 @@ protected function guardIsDeletable(EnvironmentInterface $environment, ModelIdIn } $eventDispatcher = $environment->getEventDispatcher(); + assert($eventDispatcher instanceof EventDispatcherInterface); $eventDispatcher->dispatch( new LogEvent( @@ -146,7 +153,9 @@ protected function guardIsDeletable(EnvironmentInterface $environment, ModelIdIn protected function fetchModel(EnvironmentInterface $environment, ModelIdInterface $modelId) { $dataProvider = $environment->getDataProvider($modelId->getDataProviderName()); - $model = $dataProvider->fetch($dataProvider->getEmptyConfig()->setId($modelId->getId())); + assert($dataProvider instanceof DataProviderInterface); + + $model = $dataProvider->fetch($dataProvider->getEmptyConfig()->setId($modelId->getId())); if (!$model || !$model->getId()) { throw new DcGeneralRuntimeException( @@ -171,21 +180,28 @@ protected function fetchModel(EnvironmentInterface $environment, ModelIdInterfac */ public function delete(EnvironmentInterface $environment, ModelIdInterface $modelId) { - $this->guardNotEditOnly($environment->getDataDefinition(), $modelId); + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $this->guardNotEditOnly($definition, $modelId); $this->guardIsDeletable($environment, $modelId); $model = $this->fetchModel($environment, $modelId); // Trigger event before the model will be deleted. $preDeleteEvent = new PreDeleteModelEvent($environment, $model); - $environment->getEventDispatcher()->dispatch($preDeleteEvent, $preDeleteEvent::NAME); + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $dispatcher->dispatch($preDeleteEvent, $preDeleteEvent::NAME); $dataProvider = $environment->getDataProvider($modelId->getDataProviderName()); + assert($dataProvider instanceof DataProviderInterface); $dataProvider->delete($model); // Trigger event after the model is deleted. $postDeleteEvent = new PostDeleteModelEvent($environment, $model); - $environment->getEventDispatcher()->dispatch($postDeleteEvent, $postDeleteEvent::NAME); + $dispatcher->dispatch($postDeleteEvent, $postDeleteEvent::NAME); } /** @@ -193,16 +209,19 @@ public function delete(EnvironmentInterface $environment, ModelIdInterface $mode * * @param EnvironmentInterface $environment The environment. * - * @return string + * @return string|null */ protected function process(EnvironmentInterface $environment) { - $inputProvider = $environment->getInputProvider(); + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + $dataDefinition = $environment->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); $modelId = ModelId::fromSerialized($inputProvider->getParameter('id')); - // Guard that we are in the preloaded environment. Otherwise checking the data definition could belong to + // Guard that we are in the preloaded environment. Otherwise, checking the data definition could belong to // another model. $this->guardValidEnvironment($dataDefinition, $modelId); @@ -234,15 +253,21 @@ protected function process(EnvironmentInterface $environment) */ private function checkPermission(EnvironmentInterface $environment) { - if (true === $environment->getDataDefinition()->getBasicDefinition()->isDeletable()) { + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + if (true === $definition->getBasicDefinition()->isDeletable()) { return true; } + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + return \sprintf( '
You have no permission for delete model %s.
', - ModelId::fromSerialized($environment->getInputProvider()->getParameter('id'))->getSerialized() + ModelId::fromSerialized($inputProvider->getParameter('id'))->getSerialized() ); } @@ -258,8 +283,11 @@ private function checkPermission(EnvironmentInterface $environment) */ protected function deepDelete(EnvironmentInterface $environment, ModelIdInterface $modelId) { + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + /** @var DefaultModelRelationshipDefinition $relationships */ - $relationships = $environment->getDataDefinition()->getDefinition('model-relationships'); + $relationships = $definition->getDefinition('model-relationships'); $childConditions = $relationships->getChildConditions($modelId->getDataProviderName()); @@ -271,11 +299,15 @@ protected function deepDelete(EnvironmentInterface $environment, ModelIdInterfac continue; } - $dataProvider = $environment->getDataProvider($modelId->getDataProviderName()); - $model = $dataProvider->fetch( - $dataProvider->getEmptyConfig()->setId($modelId->getId()) - ); + $dataProvider = $environment->getDataProvider($modelId->getDataProviderName()); + assert($dataProvider instanceof DataProviderInterface); + + $model = $dataProvider->fetch($dataProvider->getEmptyConfig()->setId($modelId->getId())); + assert($model instanceof ModelInterface); + $destinationChildDataProvider = $environment->getDataProvider($childCondition->getDestinationName()); + assert($destinationChildDataProvider instanceof DataProviderInterface); + /** @var DefaultCollection $destinationChildModels */ $destinationChildModels = $destinationChildDataProvider->fetchAll( @@ -291,9 +323,14 @@ protected function deepDelete(EnvironmentInterface $environment, ModelIdInterfac } foreach ($childConditions as $childCondition) { - $dataProvider = $environment->getDataProvider($modelId->getDataProviderName()); - $model = $dataProvider->fetch($dataProvider->getEmptyConfig()->setId($modelId->getId())); + $dataProvider = $environment->getDataProvider($modelId->getDataProviderName()); + assert($dataProvider instanceof DataProviderInterface); + + $model = $dataProvider->fetch($dataProvider->getEmptyConfig()->setId($modelId->getId())); + assert($model instanceof ModelInterface); + $childDataProvider = $environment->getDataProvider($childCondition->getDestinationName()); + assert($childDataProvider instanceof DataProviderInterface); $filters = $childCondition->getFilter($model); /** @var DefaultCollection $childModels */ @@ -303,15 +340,19 @@ protected function deepDelete(EnvironmentInterface $environment, ModelIdInterfac } foreach ($childModels as $childModel) { + if (null === ($dispatcher = $environment->getEventDispatcher())) { + continue; + } + // Trigger event before the model will be deleted. $preDeleteEvent = new PreDeleteModelEvent($environment, $childModel); - $environment->getEventDispatcher()->dispatch($preDeleteEvent, $preDeleteEvent::NAME); + $dispatcher->dispatch($preDeleteEvent, $preDeleteEvent::NAME); $childDataProvider->delete($childModel); // Trigger event after the model is deleted. $postDeleteEvent = new PostDeleteModelEvent($environment, $childModel); - $environment->getEventDispatcher()->dispatch($postDeleteEvent, $postDeleteEvent::NAME); + $dispatcher->dispatch($postDeleteEvent, $postDeleteEvent::NAME); } } } diff --git a/src/Contao/View/Contao2BackendView/ActionHandler/EditHandler.php b/src/Contao/View/Contao2BackendView/ActionHandler/EditHandler.php index 7751ae73..4d9f61a0 100644 --- a/src/Contao/View/Contao2BackendView/ActionHandler/EditHandler.php +++ b/src/Contao/View/Contao2BackendView/ActionHandler/EditHandler.php @@ -30,12 +30,17 @@ use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminatorAwareTrait; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\BaseView; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\EditMask; +use ContaoCommunityAlliance\DcGeneral\Data\DataProviderInterface; +use ContaoCommunityAlliance\DcGeneral\Data\ModelIdInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\BackCommand; use ContaoCommunityAlliance\DcGeneral\Data\DefaultEditInformation; use ContaoCommunityAlliance\DcGeneral\Data\ModelId; use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; use ContaoCommunityAlliance\DcGeneral\Event\ActionEvent; use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralRuntimeException; +use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; /** * Class CreateHandler @@ -100,15 +105,18 @@ public function handleEvent(ActionEvent $event) * * @param EnvironmentInterface $environment The environment. * - * @return string|bool + * @return string|false * * @throws DcGeneralRuntimeException When the requested model could not be located in the database. */ protected function process(EnvironmentInterface $environment) { $inputProvider = $environment->getInputProvider(); - $modelId = ModelId::fromSerialized($inputProvider->getParameter('id')); - $dataProvider = $environment->getDataProvider($modelId->getDataProviderName()); + assert($inputProvider instanceof InputProviderInterface); + + $modelId = ModelId::fromSerialized($inputProvider->getParameter('id')); + $dataProvider = $environment->getDataProvider($modelId->getDataProviderName()); + assert($dataProvider instanceof DataProviderInterface); $view = $environment->getView(); if (!$view instanceof BaseView) { @@ -143,11 +151,15 @@ private function checkPermission(ActionEvent $event) { $environment = $event->getEnvironment(); - if (true === $environment->getDataDefinition()->getBasicDefinition()->isEditable()) { + $dataDefinition = $environment->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + + if (true === $dataDefinition->getBasicDefinition()->isEditable()) { return true; } $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); $event->setResponse( \sprintf( @@ -168,7 +180,7 @@ private function checkPermission(ActionEvent $event) * reload of the page. * * @param EnvironmentInterface $environment The environment. - * @param ModelId $modelId The model id. + * @param ModelIdInterface $modelId The model id. * * @return void * @@ -176,12 +188,18 @@ private function checkPermission(ActionEvent $event) * * @SuppressWarnings(PHPMD.LongVariable) */ - private function checkRestoreVersion(EnvironmentInterface $environment, ModelId $modelId) + private function checkRestoreVersion(EnvironmentInterface $environment, ModelIdInterface $modelId) { $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + + $dataDefinition = $environment->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + + $dataProviderDefinition = $dataDefinition->getDataProviderDefinition(); - $dataProviderDefinition = $environment->getDataDefinition()->getDataProviderDefinition(); - $dataProvider = $environment->getDataProvider($modelId->getDataProviderName()); + $dataProvider = $environment->getDataProvider($modelId->getDataProviderName()); + assert($dataProvider instanceof DataProviderInterface); if ( !((null !== ($modelVersion = $inputProvider->getValue('version'))) @@ -191,6 +209,9 @@ private function checkRestoreVersion(EnvironmentInterface $environment, ModelId return; } + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + if (null === ($model = $dataProvider->getVersion($modelId->getId(), $modelVersion))) { $message = \sprintf( 'Could not load version %s of record ID %s from %s', @@ -199,7 +220,7 @@ private function checkRestoreVersion(EnvironmentInterface $environment, ModelId $modelId->getDataProviderName() ); - $environment->getEventDispatcher()->dispatch( + $dispatcher->dispatch( new LogEvent($message, 'ERROR', 'DC_General - checkRestoreVersion()'), ContaoEvents::SYSTEM_LOG ); @@ -209,7 +230,7 @@ private function checkRestoreVersion(EnvironmentInterface $environment, ModelId $dataProvider->save($model); $dataProvider->setVersionActive($modelId->getId(), $modelVersion); - $environment->getEventDispatcher()->dispatch(new ReloadEvent(), ContaoEvents::CONTROLLER_RELOAD); + $dispatcher->dispatch(new ReloadEvent(), ContaoEvents::CONTROLLER_RELOAD); } /** @@ -221,7 +242,12 @@ private function checkRestoreVersion(EnvironmentInterface $environment, ModelId */ protected function handleGlobalCommands(EnvironmentInterface $environment) { - $backendView = $environment->getDataDefinition()->getDefinition(Contao2BackendViewDefinitionInterface::NAME); + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $backendView = $definition->getDefinition(Contao2BackendViewDefinitionInterface::NAME); + assert($backendView instanceof Contao2BackendViewDefinitionInterface); + $globalCommands = $backendView->getGlobalCommands(); $globalCommands->clearCommands(); diff --git a/src/Contao/View/Contao2BackendView/ActionHandler/ListViewShowAllHandler.php b/src/Contao/View/Contao2BackendView/ActionHandler/ListViewShowAllHandler.php index 147c8608..8f1286e9 100644 --- a/src/Contao/View/Contao2BackendView/ActionHandler/ListViewShowAllHandler.php +++ b/src/Contao/View/Contao2BackendView/ActionHandler/ListViewShowAllHandler.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Sven Baumann * @author David Molineus - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -24,8 +25,10 @@ use ContaoCommunityAlliance\DcGeneral\Action; use ContaoCommunityAlliance\DcGeneral\Contao\DataDefinition\Definition\Contao2BackendViewDefinitionInterface; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\ContaoBackendViewTemplate; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\BasicDefinitionInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\GroupAndSortingInformationInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\ListingConfigInterface; use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; /** @@ -65,9 +68,16 @@ protected function determineTemplate($groupingInformation) */ protected function renderTemplate(ContaoBackendViewTemplate $template, EnvironmentInterface $environment) { - $dataDefinition = $environment->getDataDefinition(); - $viewDefinition = $dataDefinition->getDefinition(Contao2BackendViewDefinitionInterface::NAME); - $groupAndSorting = $viewDefinition->getListingConfig()->getGroupAndSortingDefinition(); + $dataDefinition = $environment->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + + $viewDefinition = $dataDefinition->getDefinition(Contao2BackendViewDefinitionInterface::NAME); + assert($viewDefinition instanceof Contao2BackendViewDefinitionInterface); + + $listingConfig = $viewDefinition->getListingConfig(); + assert($listingConfig instanceof ListingConfigInterface); + + $groupAndSorting = $listingConfig->getGroupAndSortingDefinition(); $pasteButton = $this->renderPasteTopButton($environment, $groupAndSorting); diff --git a/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/EditAllHandler.php b/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/EditAllHandler.php index 995c09c3..bad69dbf 100644 --- a/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/EditAllHandler.php +++ b/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/EditAllHandler.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,8 @@ * @package contao-community-alliance/dc-general * @author Sven Baumann * @author Richard Henkenjohann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0 * @filesource */ @@ -26,16 +27,20 @@ use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminatorAwareTrait; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\ContaoWidgetManager; use ContaoCommunityAlliance\DcGeneral\Data\DataProviderInterface; +use ContaoCommunityAlliance\DcGeneral\Data\EditInformationInterface; use ContaoCommunityAlliance\DcGeneral\Data\ModelId; use ContaoCommunityAlliance\DcGeneral\Data\ModelIdInterface; use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; use ContaoCommunityAlliance\DcGeneral\Data\PropertyValueBag; use ContaoCommunityAlliance\DcGeneral\Data\PropertyValueBagInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\Properties\PropertyInterface; use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; use ContaoCommunityAlliance\DcGeneral\Event\ActionEvent; +use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; use ContaoCommunityAlliance\DcGeneral\View\ActionHandler\AbstractPropertyOverrideEditAllHandler; use ContaoCommunityAlliance\DcGeneral\View\ActionHandler\CallActionTrait; +use ContaoCommunityAlliance\Translator\TranslatorInterface; /** * The class handle the "editAll" commands. @@ -60,7 +65,7 @@ public function __construct(RequestScopeDeterminator $scopeDeterminator) /** * {@inheritDoc} */ - public function handleEvent(ActionEvent $event) + public function handleEvent(ActionEvent $event): void { if ( !$this->getScopeDeterminator()->currentScopeIsBackend() @@ -69,19 +74,21 @@ public function handleEvent(ActionEvent $event) return; } - if (false !== ($response = $this->process($event->getAction(), $event->getEnvironment()))) { - $event->setResponse($response); - $event->stopPropagation(); - } + $response = $this->process($event->getAction(), $event->getEnvironment()); + $event->setResponse($response); + $event->stopPropagation(); } /** * {@inheritDoc} */ - private function process(Action $action, EnvironmentInterface $environment) + private function process(Action $action, EnvironmentInterface $environment): string { $inputProvider = $environment->getInputProvider(); - $translator = $environment->getTranslator(); + assert($inputProvider instanceof InputProviderInterface); + + $translator = $environment->getTranslator(); + assert($translator instanceof TranslatorInterface); $renderInformation = new \ArrayObject(); @@ -93,6 +100,9 @@ private function process(Action $action, EnvironmentInterface $environment) $this->handleSubmit($action, $environment); } + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + return $this->renderTemplate( $action, [ @@ -100,7 +110,7 @@ private function process(Action $action, EnvironmentInterface $environment) $translator->translate('MSC.' . $inputProvider->getParameter('mode') . 'Selected') . ': ' . $translator->translate('MSC.all.0'), 'fieldsets' => $renderInformation->offsetGet('fieldsets'), - 'table' => $environment->getDataDefinition()->getName(), + 'table' => $definition->getName(), 'error' => $renderInformation->offsetGet('error'), 'breadcrumb' => $this->renderBreadcrumb($environment), 'editButtons' => $this->getEditButtons($action, $environment), @@ -122,16 +132,22 @@ private function process(Action $action, EnvironmentInterface $environment) */ private function buildFieldSets(Action $action, \ArrayObject $renderInformation, EnvironmentInterface $environment) { - $formInputs = $environment->getInputProvider()->getValue('FORM_INPUTS'); + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + + $formInputs = $inputProvider->getValue('FORM_INPUTS'); $collection = $this->getCollectionFromSession($action, $environment); $fieldSets = []; $errors = []; while ($collection->count() > 0) { - $model = $collection->shift(); - $modelId = ModelId::fromModel($model); + if (null === ($model = $collection->shift())) { + continue; + } + $modelId = ModelId::fromModel($model); $propertyValuesBag = $this->getPropertyValueBagFromModel($action, $model, $environment); + if ($formInputs) { $this->handleEditCollection($action, $propertyValuesBag, $model, $renderInformation, $environment); } @@ -173,6 +189,8 @@ private function buildFieldSets(Action $action, \ArrayObject $renderInformation, private function handleLegendCollapsed(array $fieldSets) { $editInformation = System::getContainer()->get('cca.dc-general.edit-information'); + assert($editInformation instanceof EditInformationInterface); + if (!$editInformation->hasAnyModelError()) { return $fieldSets; } @@ -208,13 +226,20 @@ private function renderEditFields( PropertyValueBagInterface $propertyValuesBag, EnvironmentInterface $environment ) { - $properties = $environment->getDataDefinition()->getPropertiesDefinition(); + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $properties = $definition->getPropertiesDefinition(); - $selectProperties = (array) $this->getPropertiesFromSession($action, $environment); + $selectProperties = $this->getPropertiesFromSession($action, $environment); $modelId = ModelId::fromModel($model); $dataProvider = $environment->getDataProvider($modelId->getDataProviderName()); - $editModel = $dataProvider->fetch($dataProvider->getEmptyConfig()->setId($modelId->getId())); + assert($dataProvider instanceof DataProviderInterface); + + $editModel = $dataProvider->fetch($dataProvider->getEmptyConfig()->setId($modelId->getId())); + assert($editModel instanceof ModelInterface); + $visibleModel = $this->getVisibleModel($action, $editModel, $dataProvider, $environment); $fields = []; @@ -259,9 +284,12 @@ private function renderEditFields( } if (null === $fields[0]) { + $translator = $environment->getTranslator(); + assert($translator instanceof TranslatorInterface); + $fields[] = \sprintf( '

 

%s

 

', - $environment->getTranslator()->translate('MSC.no_properties_available') + $translator->translate('MSC.no_properties_available') ); } @@ -284,7 +312,7 @@ private function getVisibleModel( DataProviderInterface $dataProvider, EnvironmentInterface $environment ) { - $selectProperties = (array) $this->getPropertiesFromSession($action, $environment); + $selectProperties = $this->getPropertiesFromSession($action, $environment); $visibleModel = $dataProvider->getEmptyModel(); $visibleModel->setId($editModel->getId()); @@ -382,7 +410,9 @@ private function markModelErrors( EnvironmentInterface $environment ) { $editInformation = System::getContainer()->get('cca.dc-general.edit-information'); - $sessionValues = $this->getEditPropertiesByModelId($action, ModelId::fromModel($model), $environment); + assert($editInformation instanceof EditInformationInterface); + + $sessionValues = $this->getEditPropertiesByModelId($action, ModelId::fromModel($model), $environment); $modelError = $editInformation->getModelError($editModel); if ($modelError && isset($modelError[$selectProperty->getName()])) { @@ -421,7 +451,9 @@ private function handleEditCollection( \ArrayObject $renderInformation, EnvironmentInterface $environment ) { - $dataProvider = $environment->getDataProvider($model->getProviderName()); + $dataProvider = $environment->getDataProvider($model->getProviderName()); + assert($dataProvider instanceof DataProviderInterface); + $editCollection = $dataProvider->getEmptyCollection(); $revertCollection = $dataProvider->getEmptyCollection(); @@ -458,7 +490,7 @@ private function buildEditProperty(PropertyInterface $originalProperty, ModelIdI $editProperty->setSearchable($originalProperty->isSearchable()); $editProperty->setFilterable($originalProperty->isFilterable()); $editProperty->setWidgetType($originalProperty->getWidgetType()); - $editProperty->setOptions($originalProperty->getOptions()); + $editProperty->setOptions($originalProperty->getOptions() ?? []); $editProperty->setExplanation($originalProperty->getExplanation()); $editProperty->setExtra($originalProperty->getExtra()); diff --git a/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/OverrideAllHandler.php b/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/OverrideAllHandler.php index c438ae8d..b70cced3 100644 --- a/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/OverrideAllHandler.php +++ b/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/OverrideAllHandler.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2021 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,8 @@ * @package contao-community-alliance/dc-general * @author Sven Baumann * @author Richard Henkenjohann - * @copyright 2013-2021 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0 * @filesource */ @@ -23,20 +24,29 @@ use Contao\System; use Contao\Widget; use ContaoCommunityAlliance\DcGeneral\Action; +use ContaoCommunityAlliance\DcGeneral\Contao\Compatibility\DcCompat; use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminator; use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminatorAwareTrait; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\ContaoWidgetManager; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\EncodePropertyValueFromWidgetEvent; +use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Widget\AbstractWidget; +use ContaoCommunityAlliance\DcGeneral\Data\EditInformationInterface; use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; use ContaoCommunityAlliance\DcGeneral\Data\PropertyValueBag; use ContaoCommunityAlliance\DcGeneral\Data\PropertyValueBagInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; use ContaoCommunityAlliance\DcGeneral\Event\ActionEvent; +use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; use ContaoCommunityAlliance\DcGeneral\View\ActionHandler\AbstractPropertyOverrideEditAllHandler; use ContaoCommunityAlliance\DcGeneral\View\ActionHandler\CallActionTrait; +use ContaoCommunityAlliance\Translator\TranslatorInterface; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; /** * The class handle the "overrideAll" commands. + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class OverrideAllHandler extends AbstractPropertyOverrideEditAllHandler { @@ -56,7 +66,7 @@ public function __construct(RequestScopeDeterminator $scopeDeterminator) /** * {@inheritDoc} */ - public function handleEvent(ActionEvent $event) + public function handleEvent(ActionEvent $event): void { if ( !$this->getScopeDeterminator()->currentScopeIsBackend() @@ -65,7 +75,7 @@ public function handleEvent(ActionEvent $event) return; } - if (false !== ($response = $this->process($event->getAction(), $event->getEnvironment()))) { + if ('' !== ($response = $this->process($event->getAction(), $event->getEnvironment()))) { $event->setResponse($response); $event->stopPropagation(); } @@ -83,9 +93,14 @@ public function handleEvent(ActionEvent $event) */ private function process(Action $action, EnvironmentInterface $environment) { - $inputProvider = $environment->getInputProvider(); - $translator = $environment->getTranslator(); + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + + $translator = $environment->getTranslator(); + assert($translator instanceof TranslatorInterface); + $editInformation = System::getContainer()->get('cca.dc-general.edit-information'); + assert($editInformation instanceof EditInformationInterface); $renderInformation = new \ArrayObject(); @@ -109,6 +124,9 @@ private function process(Action $action, EnvironmentInterface $environment) $this->handleSubmit($action, $environment); } + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + return $this->renderTemplate( $action, [ @@ -116,11 +134,11 @@ private function process(Action $action, EnvironmentInterface $environment) $translator->translate('MSC.' . $inputProvider->getParameter('mode') . 'Selected') . ': ' . $translator->translate('MSC.all.0'), 'fieldsets' => $renderInformation->offsetGet('fieldsets'), - 'table' => $environment->getDataDefinition()->getName(), + 'table' => $definition->getName(), 'error' => $renderInformation->offsetGet('error'), 'breadcrumb' => $this->renderBreadcrumb($environment), 'editButtons' => $this->getEditButtons($action, $environment), - 'noReload' => (bool) $editInformation->hasAnyModelError() + 'noReload' => $editInformation->hasAnyModelError() ] ); } @@ -152,6 +170,7 @@ protected function handleInvalidPropertyValueBag( } $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); foreach (\array_keys($propertyValueBag->getArrayCopy()) as $propertyName) { $allErrors = $propertyValueBag->getPropertyValueErrors($propertyName); @@ -172,7 +191,11 @@ protected function handleInvalidPropertyValueBag( $event = new EncodePropertyValueFromWidgetEvent($environment, $model, $eventPropertyValueBag); $event->setProperty($propertyName) ->setValue($inputProvider->getValue($propertyName, true)); - $environment->getEventDispatcher()->dispatch($event, EncodePropertyValueFromWidgetEvent::NAME); + + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $dispatcher->dispatch($event, EncodePropertyValueFromWidgetEvent::NAME); $propertyValueBag->setPropertyValue($propertyName, $event->getValue()); @@ -239,17 +262,17 @@ private function getOverrideProperties(Action $action, EnvironmentInterface $env /** * Render the field sets. * - * @param Action $action The action. - * @param \ArrayObject $renderInformation The render information. - * @param PropertyValueBagInterface|null $propertyValues The property values. - * @param EnvironmentInterface $environment The environment. + * @param Action $action The action. + * @param \ArrayObject $renderInformation The render information. + * @param PropertyValueBagInterface $propertyValues The property values. + * @param EnvironmentInterface $environment The environment. * * @return void */ private function renderFieldSets( Action $action, \ArrayObject $renderInformation, - PropertyValueBagInterface $propertyValues = null, + PropertyValueBagInterface $propertyValues, EnvironmentInterface $environment ) { $properties = $this->getOverrideProperties($action, $environment); @@ -260,7 +283,7 @@ private function renderFieldSets( $errors = []; $fieldSet = ['palette' => '', 'class' => 'tl_box']; - $propertyNames = $propertyValues ? \array_keys($propertyValues->getArrayCopy()) : \array_keys($properties); + $propertyNames = \array_keys($propertyValues->getArrayCopy()); foreach ($propertyNames as $propertyName) { $errors = $this->getPropertyValueErrors($propertyValues, $propertyName, $errors); @@ -274,12 +297,13 @@ private function renderFieldSets( $this->setDefaultValue($model, $propertyValues, $propertyName, $environment); $widget = $widgetManager->getWidget($property->getName(), $propertyValues); + assert($widget instanceof Widget); $widgetModel = $this->getModelFromWidget($widget); if (!$this->ensurePropertyVisibleInModel($action, $property->getName(), $widgetModel, $environment)) { $fieldSet['palette'] .= - $this->injectSelectParentPropertyInformation($action, $property, $widgetModel, $environment); + $this->injectSelectParentPropertyInformation($action, $property, $widgetModel, $environment) ?? ''; continue; } @@ -299,13 +323,16 @@ private function renderFieldSets( $widgetModel, $propertyValues, $environment - ); + ) ?? ''; } + $translator = $environment->getTranslator(); + assert($translator instanceof TranslatorInterface); + if (empty($fieldSet['palette'])) { $fieldSet['palette'] = \sprintf( '

 

%s

 

', - $environment->getTranslator()->translate('MSC.no_properties_available') + $translator->translate('MSC.no_properties_available') ); } @@ -320,13 +347,16 @@ private function renderFieldSets( * * @return ModelInterface */ - private function getModelFromWidget(Widget $widget) + private function getModelFromWidget(Widget $widget): ModelInterface { - if ($widget->dataContainer) { + if ($widget->dataContainer instanceof DcCompat) { return $widget->dataContainer->getModel(); } + if ($widget instanceof AbstractWidget) { + return $widget->getModel(); + } - return $widget->getModel(); + throw new \InvalidArgumentException('Expected an instance of ' . AbstractWidget::class); } /** @@ -338,11 +368,13 @@ private function getModelFromWidget(Widget $widget) * * @return array */ - private function getPropertyValueErrors(PropertyValueBagInterface $propertyValueBag, $propertyName, array $errors) - { + private function getPropertyValueErrors( + PropertyValueBagInterface $propertyValueBag, + string $propertyName, + array $errors + ): array { if ( - (null !== $propertyValueBag) - && $propertyValueBag->hasPropertyValue($propertyName) + $propertyValueBag->hasPropertyValue($propertyName) && $propertyValueBag->isPropertyValueInvalid($propertyName) ) { $errors = \array_merge( @@ -367,10 +399,13 @@ private function getPropertyValueErrors(PropertyValueBagInterface $propertyValue private function setDefaultValue( ModelInterface $model, PropertyValueBagInterface $propertyValueBag, - $propertyName, + string $propertyName, EnvironmentInterface $environment - ) { - $propertiesDefinition = $environment->getDataDefinition()->getPropertiesDefinition(); + ): void { + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $propertiesDefinition = $definition->getPropertiesDefinition(); // If in the intersect model the value available, then set it as default. if ($modelValue = $model->getProperty($propertyName)) { @@ -381,7 +416,8 @@ private function setDefaultValue( if ( $propertiesDefinition->hasProperty($propertyName) - && !$environment->getInputProvider()->hasValue($propertyName) + && null !== ($inputProvider = $environment->getInputProvider()) + && !$inputProvider->hasValue($propertyName) ) { $propertyValueBag->setPropertyValue( $propertyName, diff --git a/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/PasteAllHandler.php b/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/PasteAllHandler.php index 3b34d451..ef26626b 100644 --- a/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/PasteAllHandler.php +++ b/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/PasteAllHandler.php @@ -20,24 +20,33 @@ namespace ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\ActionHandler\MultipleHandler; +use ContaoCommunityAlliance\DcGeneral\Clipboard\ClipboardInterface; use ContaoCommunityAlliance\DcGeneral\Clipboard\Filter; use ContaoCommunityAlliance\DcGeneral\Clipboard\ItemInterface; use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminator; use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminatorAwareTrait; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\ViewHelpers; use ContaoCommunityAlliance\DcGeneral\Data\CollectionInterface; +use ContaoCommunityAlliance\DcGeneral\Data\DataProviderInterface; use ContaoCommunityAlliance\DcGeneral\Data\ModelId; use ContaoCommunityAlliance\DcGeneral\Data\ModelIdInterface; +use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\BasicDefinitionInterface; use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; use ContaoCommunityAlliance\DcGeneral\Event\ActionEvent; use ContaoCommunityAlliance\DcGeneral\Event\PostDuplicateModelEvent; use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralInvalidArgumentException; +use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; use ContaoCommunityAlliance\DcGeneral\View\ActionHandler\CallActionTrait; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; /** * Action handler for paste all action. * * @return void + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class PasteAllHandler { @@ -47,16 +56,16 @@ class PasteAllHandler /** * The copied model is available by paste mode copy. * - * @var ModelIdInterface + * @var ModelInterface|null */ - protected $copiedModel; + protected $copiedModel = null; /** * The original model is available by paste mode copy. * - * @var ModelIdInterface + * @var ModelInterface|null */ - protected $originalModel; + protected $originalModel = null; /** @@ -72,7 +81,7 @@ public function __construct(RequestScopeDeterminator $scopeDeterminator) /** * {@inheritDoc} */ - public function handleEvent(ActionEvent $event) + public function handleEvent(ActionEvent $event): void { if ( !$this->getScopeDeterminator()->currentScopeIsBackend() @@ -101,7 +110,10 @@ private function process(EnvironmentInterface $environment) } $inputProvider = $environment->getInputProvider(); - $clipboard = $environment->getClipboard(); + assert($inputProvider instanceof InputProviderInterface); + + $clipboard = $environment->getClipboard(); + assert($clipboard instanceof ClipboardInterface); $inputProvider->setParameter('pasteAll', true); @@ -131,17 +143,30 @@ private function process(EnvironmentInterface $environment) */ protected function getClipboardItems(EnvironmentInterface $environment) { - $basicDefinition = $environment->getDataDefinition()->getBasicDefinition(); + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $basicDefinition = $definition->getBasicDefinition(); + assert($basicDefinition instanceof BasicDefinitionInterface); + + $provider = $basicDefinition->getDataProvider(); + assert(\is_string($provider)); + + $parentProvider = $basicDefinition->getParentDataProvider(); + assert(\is_string($parentProvider)); $filter = new Filter(); - $filter->andModelIsFromProvider($basicDefinition->getDataProvider()); + $filter->andModelIsFromProvider($provider); if ($basicDefinition->getParentDataProvider()) { - $filter->andParentIsFromProvider($basicDefinition->getParentDataProvider()); + $filter->andParentIsFromProvider($parentProvider); } else { $filter->andHasNoParent(); } - return $environment->getClipboard()->fetch($filter); + $clipboard = $environment->getClipboard(); + assert($clipboard instanceof ClipboardInterface); + + return $clipboard->fetch($filter); } /** @@ -154,6 +179,8 @@ protected function getClipboardItems(EnvironmentInterface $environment) protected function getCollection(EnvironmentInterface $environment) { $dataDefinition = $environment->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + $relationShip = $dataDefinition->getModelRelationshipDefinition(); if (!$relationShip->getChildCondition($dataDefinition->getName(), $dataDefinition->getName())) { @@ -173,6 +200,7 @@ protected function getCollection(EnvironmentInterface $environment) protected function getFlatCollection(EnvironmentInterface $environment) { $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); $previousItem = null; $collection = []; @@ -207,9 +235,15 @@ protected function getFlatCollection(EnvironmentInterface $environment) */ protected function getHierarchyCollection(array $clipboardItems, EnvironmentInterface $environment) { - $dataProvider = $environment->getDataProvider(); + $dataProvider = $environment->getDataProvider(); + assert($dataProvider instanceof DataProviderInterface); + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + $dataDefinition = $environment->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + $relationShip = $dataDefinition->getModelRelationshipDefinition(); $childCondition = $relationShip->getChildCondition($dataDefinition->getName(), $dataDefinition->getName()); if (null === $childCondition) { @@ -241,19 +275,19 @@ protected function getHierarchyCollection(array $clipboardItems, EnvironmentInte $previousItem = $clipboardItem; - $model = - $dataProvider->fetch($dataProvider->getEmptyConfig()->setId($modelId->getId())); + $model = $dataProvider->fetch($dataProvider->getEmptyConfig()->setId($modelId->getId())); + assert($model instanceof ModelInterface); $itemCollection = $dataProvider->fetchAll($dataProvider->getEmptyConfig()->setFilter($childCondition->getFilter($model))); - if ($itemCollection) { - $collection = $this->setSubItemsToCollection( - $clipboardItem, - $this->getSubClipboardItems($clipboardItems, $itemCollection), - $collection, - $environment - ); - } + assert($itemCollection instanceof CollectionInterface); + + $collection = $this->setSubItemsToCollection( + $clipboardItem, + $this->getSubClipboardItems($clipboardItems, $itemCollection), + $collection, + $environment + ); } return $collection; @@ -307,7 +341,11 @@ protected function setSubItemsToCollection( } $dataProvider = $environment->getDataProvider(); + assert($dataProvider instanceof DataProviderInterface); + $dataDefinition = $environment->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + $relationShip = $dataDefinition->getModelRelationshipDefinition(); $childCondition = $relationShip->getChildCondition($dataDefinition->getName(), $dataDefinition->getName()); if (null === $childCondition) { @@ -338,19 +376,19 @@ protected function setSubItemsToCollection( 'pasteMode' => $intoItem ? 'after' : 'into' ]; - $model = - $dataProvider->fetch($dataProvider->getEmptyConfig()->setId($modelId->getId())); + $model = $dataProvider->fetch($dataProvider->getEmptyConfig()->setId($modelId->getId())); + assert($model instanceof ModelInterface); + $itemCollection = $dataProvider->fetchAll($dataProvider->getEmptyConfig()->setFilter($childCondition->getFilter($model))); + assert($itemCollection instanceof CollectionInterface); - if ($itemCollection) { - $collection = $this->setSubItemsToCollection( - $subClipboardItem, - $this->getSubClipboardItems($this->getClipboardItems($environment), $itemCollection), - $collection, - $environment - ); - } + $collection = $this->setSubItemsToCollection( + $subClipboardItem, + $this->getSubClipboardItems($this->getClipboardItems($environment), $itemCollection), + $collection, + $environment + ); } return $collection; @@ -365,7 +403,10 @@ protected function setSubItemsToCollection( */ protected function addDispatchDuplicateModel(EnvironmentInterface $environment) { - $environment->getEventDispatcher()->addListener( + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $dispatcher->addListener( PostDuplicateModelEvent::NAME, function (PostDuplicateModelEvent $event) { $this->copiedModel = $event->getModel(); @@ -385,6 +426,8 @@ function (PostDuplicateModelEvent $event) { protected function setParameterForPaste(array $collectionItem, EnvironmentInterface $environment) { $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + $clipboardItem = $collectionItem['item']; $inputProvider->unsetParameter('after'); @@ -405,6 +448,7 @@ protected function setParameterForPaste(array $collectionItem, EnvironmentInterf return; } + assert($this->copiedModel instanceof ModelInterface); $copiedModelId = ModelId::fromModel($this->copiedModel); $inputProvider->setParameter($collectionItem['pasteMode'], $copiedModelId->getSerialized()); diff --git a/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/SelectModelAllHandler.php b/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/SelectModelAllHandler.php index 56b22af9..92d5a52d 100644 --- a/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/SelectModelAllHandler.php +++ b/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/SelectModelAllHandler.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -12,7 +12,7 @@ * * @package contao-community-alliance/dc-general * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0 * @filesource */ @@ -24,6 +24,7 @@ use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminatorAwareTrait; use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; use ContaoCommunityAlliance\DcGeneral\Event\ActionEvent; +use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; use ContaoCommunityAlliance\DcGeneral\View\ActionHandler\CallActionTrait; /** @@ -60,7 +61,7 @@ public function handleEvent(ActionEvent $event) return; } - if (false !== ($response = $this->process($event->getAction(), $event->getEnvironment()))) { + if ('' !== ($response = $this->process($event->getAction(), $event->getEnvironment()))) { $event->setResponse($response); } } @@ -76,6 +77,7 @@ public function handleEvent(ActionEvent $event) private function process(Action $action, EnvironmentInterface $environment) { $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); return $this->callAction( $environment, @@ -87,6 +89,6 @@ private function process(Action $action, EnvironmentInterface $environment) 'select' => $inputProvider->getParameter('select') ] ) - ); + ) ?? ''; } } diff --git a/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/SelectPropertyAllHandler.php b/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/SelectPropertyAllHandler.php index acbdfcc6..6d10da9c 100644 --- a/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/SelectPropertyAllHandler.php +++ b/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/SelectPropertyAllHandler.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2022 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,7 @@ * @package contao-community-alliance/dc-general * @author Sven Baumann * @author Ingolf Steinhardt - * @copyright 2013-2022 Contao Community Alliance. + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0 * @filesource */ @@ -31,6 +31,7 @@ use ContaoCommunityAlliance\DcGeneral\Data\DataProviderInterface; use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; use ContaoCommunityAlliance\DcGeneral\Data\NoOpDataProvider; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\BasicDefinitionInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\Properties\DefaultProperty; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\Properties\PropertyInterface; @@ -38,6 +39,9 @@ use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; use ContaoCommunityAlliance\DcGeneral\Event\ActionEvent; use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralRuntimeException; +use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; +use ContaoCommunityAlliance\DcGeneral\SessionStorageInterface; +use ContaoCommunityAlliance\Translator\TranslatorInterface; /** * This class handles the rendering of list view "showAllProperties" actions. @@ -64,11 +68,10 @@ public function handleEvent(ActionEvent $event) !$this->getScopeDeterminator()->currentScopeIsBackend() || ('selectPropertyAll' !== $event->getAction()->getName()) ) { - return null; + return; } - $response = $this->process($event->getAction(), $event->getEnvironment()); - if (false !== $response) { + if (null !== $response = $this->process($event->getAction(), $event->getEnvironment())) { $event->setResponse($response); } } @@ -79,10 +82,13 @@ public function handleEvent(ActionEvent $event) protected function process(Action $action, EnvironmentInterface $environment) { $dataDefinition = $environment->getDataDefinition(); - $backendView = $dataDefinition->getDefinition(Contao2BackendViewDefinitionInterface::NAME); + assert($dataDefinition instanceof ContainerInterface); + + $backendView = $dataDefinition->getDefinition(Contao2BackendViewDefinitionInterface::NAME); + assert($backendView instanceof Contao2BackendViewDefinitionInterface); $backendView->getListingConfig()->setShowColumns(false); - $environment->getDataDefinition()->getBasicDefinition()->setMode(BasicDefinitionInterface::MODE_FLAT); + $dataDefinition->getBasicDefinition()->setMode(BasicDefinitionInterface::MODE_FLAT); return parent::process($action, $environment); } @@ -112,7 +118,10 @@ protected function loadCollection(EnvironmentInterface $environment) */ private function getPropertyDataProvider(EnvironmentInterface $environment) { - $providerName = 'property.' . $environment->getDataDefinition()->getName(); + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $providerName = 'property.' . $definition->getName(); $dataProvider = new NoOpDataProvider(); $dataProvider->setBaseConfig(['name' => $providerName]); @@ -132,13 +141,19 @@ private function getPropertyDataProvider(EnvironmentInterface $environment) */ private function setPropertyLabelFormatter($providerName, EnvironmentInterface $environment) { - $properties = $environment->getDataDefinition()->getPropertiesDefinition(); + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $properties = $definition->getPropertiesDefinition(); $labelFormatter = (new DefaultModelFormatterConfig()) ->setPropertyNames(['name', 'description']) ->setFormat('%s [%s]'); - $this->getViewSection($environment->getDataDefinition()) + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $this->getViewSection($definition) ->getListingConfig() ->setLabelFormatter($providerName, $labelFormatter); @@ -154,7 +169,7 @@ private function setPropertyLabelFormatter($providerName, EnvironmentInterface $ } /** - * Return the field collection for each properties. + * Return the field collection for each property. * * @param DataProviderInterface $dataProvider The field data provider. * @param EnvironmentInterface $environment The environment. @@ -165,7 +180,10 @@ private function getCollection(DataProviderInterface $dataProvider, EnvironmentI { $collection = $dataProvider->getEmptyCollection(); - foreach ($environment->getDataDefinition()->getPropertiesDefinition() as $property) { + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + foreach ($definition->getPropertiesDefinition() as $property) { if (!$this->isPropertyAllowed($property, $environment)) { continue; } @@ -205,17 +223,21 @@ private function isPropertyAllowed(PropertyInterface $property, EnvironmentInter } $translator = $environment->getTranslator(); + assert($translator instanceof TranslatorInterface); - $extra = (array) $property->getExtra(); + $extra = $property->getExtra(); if ( $this->isPropertyAllowedByEdit($extra, $environment) || $this->isPropertyAllowedByOverride($extra, $environment) ) { + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + Message::addInfo( \sprintf( $translator->translate('MSC.not_allowed_property_info'), $property->getLabel() ?: $property->getName(), - $translator->translate('MSC.' . $environment->getInputProvider()->getParameter('mode') . 'Selected') + $translator->translate('MSC.' . $inputProvider->getParameter('mode') . 'Selected') ) ); @@ -235,8 +257,11 @@ private function isPropertyAllowed(PropertyInterface $property, EnvironmentInter */ private function isPropertyAllowedByEdit(array $extra, EnvironmentInterface $environment) { + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + return (true === ($extra['doNotEditMultiple'] ?? false)) - && ('edit' === $environment->getInputProvider()->getParameter('mode')); + && ('edit' === $inputProvider->getParameter('mode')); } /** @@ -249,10 +274,13 @@ private function isPropertyAllowedByEdit(array $extra, EnvironmentInterface $env */ private function isPropertyAllowedByOverride(array $extra, EnvironmentInterface $environment) { + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + return ((true === ($extra['unique'] ?? false)) || isset($extra['readonly']) || (true === ($extra['doNotOverrideMultiple'] ?? false))) - && ('override' === $environment->getInputProvider()->getParameter('mode')); + && ('override' === $inputProvider->getParameter('mode')); } /** @@ -268,8 +296,13 @@ private function isPropertyAllowedByIntersectProperties( EnvironmentInterface $environment ) { $sessionStorage = $environment->getSessionStorage(); + assert($sessionStorage instanceof SessionStorageInterface); + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + $dataDefinition = $environment->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); $session = $sessionStorage->get($dataDefinition->getName() . '.' . $inputProvider->getParameter('mode')); @@ -341,8 +374,11 @@ private function handlePropertyFileTreeOrder(PropertyInterface $property, ModelI */ protected function renderTemplate(ContaoBackendViewTemplate $template, EnvironmentInterface $environment) { - $inputProvider = $environment->getInputProvider(); + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + $dataDefinition = $environment->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); $languageDomain = 'contao_' . $dataDefinition->getName(); @@ -388,7 +424,10 @@ protected function renderTemplate(ContaoBackendViewTemplate $template, Environme */ protected function getSelectButtons(EnvironmentInterface $environment) { - $languageDomain = 'contao_' . $environment->getDataDefinition()->getName(); + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $languageDomain = 'contao_' . $definition->getName(); $confirmMessage = \htmlentities( \sprintf( @@ -403,29 +442,34 @@ protected function getSelectButtons(EnvironmentInterface $environment) 'BackendGeneral.hideMessage(); return false;' ) ); - $onClick = 'BackendGeneral.confirmSelectOverrideEditAll(this, \'properties[]\', \'' . - $confirmMessage . '\'); return false;'; - - $continueName = $environment->getInputProvider()->getParameter('mode'); - $buttons['continue'] = \sprintf( - '', - $continueName, - $continueName, - 'c', - StringUtil::specialchars($this->translate('MSC.continue', $languageDomain)), - $onClick - ); - return $buttons; + $onClick = 'BackendGeneral.confirmSelectOverrideEditAll(this, \'properties[]\', \'' . + $confirmMessage . '\'); return false;'; + + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + + $continueName = $inputProvider->getParameter('mode'); + + return [ + 'continue' => \sprintf( + '', + $continueName, + $continueName, + 'c', + StringUtil::specialchars($this->translate('MSC.continue', $languageDomain)), + $onClick + ) + ]; } /** * Check if the action should be handled. * - * @param string $mode The list mode. + * @param int $mode The list mode. * @param Action $action The action. * - * @return mixed + * @return bool */ protected function wantToHandle($mode, Action $action) { diff --git a/src/Contao/View/Contao2BackendView/ActionHandler/ParentedListViewShowAllHandler.php b/src/Contao/View/Contao2BackendView/ActionHandler/ParentedListViewShowAllHandler.php index 6cfd4091..5a7f00b7 100644 --- a/src/Contao/View/Contao2BackendView/ActionHandler/ParentedListViewShowAllHandler.php +++ b/src/Contao/View/Contao2BackendView/ActionHandler/ParentedListViewShowAllHandler.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2022 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -16,7 +16,7 @@ * @author David Molineus * @author Richard Henkenjohann * @author Ingolf Steinhardt - * @copyright 2013-2022 Contao Community Alliance. + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -31,13 +31,18 @@ use ContaoCommunityAlliance\Contao\Bindings\Events\Date\ParseDateEvent; use ContaoCommunityAlliance\Contao\Bindings\Events\Image\GenerateHtmlEvent; use ContaoCommunityAlliance\DcGeneral\Action; +use ContaoCommunityAlliance\DcGeneral\BaseConfigRegistryInterface; +use ContaoCommunityAlliance\DcGeneral\Clipboard\ClipboardInterface; use ContaoCommunityAlliance\DcGeneral\Clipboard\Filter; use ContaoCommunityAlliance\DcGeneral\Clipboard\ItemInterface; use ContaoCommunityAlliance\DcGeneral\Contao\DataDefinition\Definition\Contao2BackendViewDefinitionInterface; +use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\BackendViewInterface; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\ContaoBackendViewTemplate; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\GetParentHeaderEvent; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\ParentViewChildRecordEvent; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\ViewHelpers; +use ContaoCommunityAlliance\DcGeneral\Data\CollectionInterface; +use ContaoCommunityAlliance\DcGeneral\Data\DataProviderInterface; use ContaoCommunityAlliance\DcGeneral\Data\ModelId; use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; @@ -47,6 +52,10 @@ use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\GroupAndSortingInformationInterface; use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralRuntimeException; +use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; +use ContaoCommunityAlliance\DcGeneral\Panel\PanelContainerInterface; +use ContaoCommunityAlliance\DcGeneral\View\ViewInterface; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; /** * This class handles the rendering of parented list view "showAll" actions. @@ -72,19 +81,20 @@ protected function wantToHandle($mode, Action $action) protected function renderModel(ModelInterface $model, EnvironmentInterface $environment) { $event = new ParentViewChildRecordEvent($environment, $model); - $environment->getEventDispatcher()->dispatch($event, $event::NAME); - - if (null !== $event->getHtml()) { - $information = [ - [ - 'colspan' => 1, - 'class' => 'tl_file_list col_1', - 'content' => $event->getHtml() - ] - ]; - $model->setMeta($model::LABEL_VALUE, $information); - return; - } + + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $dispatcher->dispatch($event, $event::NAME); + + $information = [ + [ + 'colspan' => 1, + 'class' => 'tl_file_list col_1', + 'content' => $event->getHtml() + ] + ]; + $model->setMeta($model::LABEL_VALUE, $information); parent::renderModel($model, $environment); } @@ -129,7 +139,10 @@ protected function renderTemplate(ContaoBackendViewTemplate $template, Environme */ protected function loadParentModel(EnvironmentInterface $environment) { - $pidDetails = ModelId::fromSerialized($environment->getInputProvider()->getParameter('pid')); + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + + $pidDetails = ModelId::fromSerialized($inputProvider->getParameter('pid')); if (!($provider = $environment->getDataProvider($pidDetails->getDataProviderName()))) { throw new DcGeneralRuntimeException( @@ -160,9 +173,17 @@ protected function loadParentModel(EnvironmentInterface $environment) private function renderHeaderFields($parentModel, EnvironmentInterface $environment) { $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + $parentName = $definition->getBasicDefinition()->getParentDataProvider(); - $add = []; - $properties = $environment->getParentDataDefinition()->getPropertiesDefinition(); + assert(\is_string($parentName)); + + $add = []; + + $parentDefinition = $environment->getParentDataDefinition(); + assert($parentDefinition instanceof ContainerInterface); + + $properties = $parentDefinition->getPropertiesDefinition(); foreach ($this->getViewSection($definition)->getListingConfig()->getHeaderPropertyNames() as $field) { $value = StringUtil::deserialize($parentModel->getProperty($field)); @@ -181,9 +202,12 @@ private function renderHeaderFields($parentModel, EnvironmentInterface $environm $event = new GetParentHeaderEvent($environment, $parentModel); $event->setAdditional($add); - $environment->getEventDispatcher()->dispatch($event, GetParentHeaderEvent::NAME); + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $dispatcher->dispatch($event, GetParentHeaderEvent::NAME); - if (null !== !$event->getAdditional()) { + if ($event->getAdditional()) { $add = $event->getAdditional(); } @@ -204,7 +228,7 @@ function ($value) { * @param string $field The field name. * @param string $parentName The parent definition name. * - * @return array|string + * @return string */ private function translateHeaderColumnName($field, $parentName) { @@ -239,11 +263,11 @@ private function renderParentProperty(EnvironmentInterface $environment, $proper : $value; $options = $property->getOptions(); - if ((isset($evaluation['isAssociative']) && $evaluation['isAssociative']) || ArrayUtil::isAssoc($options)) { + if (\is_array($options) && (($evaluation['isAssociative'] ?? false) || ArrayUtil::isAssoc($options))) { $value = $options[$value]; } - return $value; + return $value ?? ''; } /** @@ -304,7 +328,11 @@ private function renderForDateTime( $isRendered = true; $event = new ParseDateEvent($value, Config::get($evaluation['rgxp'] . 'Format')); - $environment->getEventDispatcher()->dispatch($event, ContaoEvents::DATE_PARSE); + + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $dispatcher->dispatch($event, ContaoEvents::DATE_PARSE); return $event->getResult(); } @@ -342,12 +370,25 @@ private function renderReference($value, $reference, &$isRendered) */ private function getParentModelButtons($parentModel, EnvironmentInterface $environment) { - if ('select' === $environment->getInputProvider()->getParameter('act')) { + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + + if ('select' === $inputProvider->getParameter('act')) { return ''; } - $config = $environment->getBaseConfigRegistry()->getBaseConfig(); - $environment->getView()->getPanel()->initialize($config); + $registry = $environment->getBaseConfigRegistry(); + assert($registry instanceof BaseConfigRegistryInterface); + + $view = $environment->getView(); + assert($view instanceof BackendViewInterface); + + $panel = $view->getPanel(); + assert($panel instanceof PanelContainerInterface); + + $config = $registry->getBaseConfig(); + + $panel->initialize($config); if (!$config->getSorting()) { return ''; } @@ -373,23 +414,33 @@ private function getParentModelButtons($parentModel, EnvironmentInterface $envir protected function getHeaderEditButton(ModelInterface $parentModel, EnvironmentInterface $environment) { $parentDefinition = $environment->getParentDataDefinition(); + assert($parentDefinition instanceof ContainerInterface); + + $backendView = $parentDefinition->getDefinition(Contao2BackendViewDefinitionInterface::NAME); + assert($backendView instanceof Contao2BackendViewDefinitionInterface); + /** @var CommandCollectionInterface $commands */ - $commands = $parentDefinition - ->getDefinition(Contao2BackendViewDefinitionInterface::NAME) - ->getModelCommands(); + $commands = $backendView->getModelCommands(); if (!$commands->hasCommandNamed('edit') || !$parentDefinition->getBasicDefinition()->isEditable()) { return null; } - $parentName = $environment->getDataDefinition()->getBasicDefinition()->getParentDataProvider(); + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $parentName = $definition->getBasicDefinition()->getParentDataProvider(); $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); $command = $commands->getCommandNamed('edit'); $parameters = (array) $command->getParameters(); // This should be set in command builder rather than here. - $parameters['do'] = $environment->getInputProvider()->getParameter('do'); + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + + $parameters['do'] = $inputProvider->getParameter('do'); $parameters['table'] = $parentName; $parameters['pid'] = ''; @@ -418,7 +469,7 @@ protected function getHeaderEditButton(ModelInterface $parentModel, EnvironmentI $href = ''; foreach ($parameters as $key => $value) { - $href .= \sprintf('&%s=%s', $key, $value); + $href .= \sprintf('&%s=%s', $key, $value ?? ''); } /** @var AddToUrlEvent $urlAfter */ $urlAfter = $dispatcher->dispatch(new AddToUrlEvent($href), ContaoEvents::BACKEND_ADD_TO_URL); @@ -429,7 +480,7 @@ protected function getHeaderEditButton(ModelInterface $parentModel, EnvironmentI StringUtil::specialchars( \sprintf($this->translate('editheader.1', $parentDefinition->getName()), $parentModel->getId()) ), - $imageEvent->getHtml() + $imageEvent->getHtml() ?? '' ); } @@ -443,23 +494,36 @@ protected function getHeaderEditButton(ModelInterface $parentModel, EnvironmentI */ private function getHeaderPasteNewButton(ModelInterface $parentModel, EnvironmentInterface $environment) { - $basicDefinition = $environment->getDataDefinition()->getBasicDefinition(); + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $basicDefinition = $definition->getBasicDefinition(); + assert($basicDefinition instanceof BasicDefinitionInterface); + if (!$basicDefinition->isCreatable()) { return null; } + $dataProvider = $basicDefinition->getDataProvider(); + assert(\is_string($dataProvider)); + $filter = new Filter(); - $filter->andModelIsFromProvider($basicDefinition->getDataProvider()); + $filter->andModelIsFromProvider($dataProvider); if ($parentProviderName = $basicDefinition->getParentDataProvider()) { $filter->andParentIsFromProvider($parentProviderName); } else { $filter->andHasNoParent(); } - if ($environment->getClipboard()->isNotEmpty($filter)) { + $clipboard = $environment->getClipboard(); + assert($clipboard instanceof ClipboardInterface); + + if ($clipboard->isNotEmpty($filter)) { return null; } + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); /** @var AddToUrlEvent $urlEvent */ $urlEvent = $dispatcher->dispatch( @@ -468,6 +532,8 @@ private function getHeaderPasteNewButton(ModelInterface $parentModel, Environmen ); $parentDefinition = $environment->getParentDataDefinition(); + assert($parentDefinition instanceof ContainerInterface); + /** @var GenerateHtmlEvent $imageEvent */ $imageEvent = $dispatcher->dispatch( new GenerateHtmlEvent( @@ -481,7 +547,7 @@ private function getHeaderPasteNewButton(ModelInterface $parentModel, Environmen '%s', $urlEvent->getUrl(), StringUtil::specialchars($this->translate('pastenew.0', $parentDefinition->getName())), - $imageEvent->getHtml() + $imageEvent->getHtml() ?? '' ); } @@ -495,14 +561,25 @@ private function getHeaderPasteNewButton(ModelInterface $parentModel, Environmen */ private function getHeaderPasteTopButton(ModelInterface $parentModel, EnvironmentInterface $environment) { - $definition = $environment->getDataDefinition(); + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + $basicDefinition = $definition->getBasicDefinition(); + assert($basicDefinition instanceof BasicDefinitionInterface); + + $dataProvider = $basicDefinition->getDataProvider(); + assert(\is_string($dataProvider)); + + $dataParentProvider = $basicDefinition->getParentDataProvider(); + assert(\is_string($dataParentProvider)); $filter = new Filter(); - $filter->andModelIsFromProvider($basicDefinition->getDataProvider()); - $filter->andParentIsFromProvider($basicDefinition->getParentDataProvider()); + $filter->andModelIsFromProvider($dataProvider); + $filter->andParentIsFromProvider($dataParentProvider); $clipboard = $environment->getClipboard(); + assert($clipboard instanceof ClipboardInterface); + if ($clipboard->isEmpty($filter)) { return null; } @@ -513,22 +590,33 @@ private function getHeaderPasteTopButton(ModelInterface $parentModel, Environmen $subFilter->andParentIsNot(ModelId::fromModel($parentModel)); $subFilter->orActionIsIn([ItemInterface::COPY, ItemInterface::DEEP_COPY]); + $dataProvider = $basicDefinition->getDataProvider(); + assert(\is_string($dataProvider)); + + $dataParentProvider = $basicDefinition->getParentDataProvider(); + assert(is_string($dataParentProvider)); + $filter = new Filter(); - $filter->andModelIsFromProvider($basicDefinition->getDataProvider()); - $filter->andParentIsFromProvider($basicDefinition->getParentDataProvider()); + $filter->andModelIsFromProvider($dataProvider); + $filter->andParentIsFromProvider($dataParentProvider); $filter->andSub($subFilter); $allowPasteTop = (bool) $clipboard->fetch($filter); } $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $dataProvider = $basicDefinition->getDataProvider(); + assert(\is_string($dataProvider)); + if ($allowPasteTop) { /** @var AddToUrlEvent $urlEvent */ $urlEvent = $dispatcher->dispatch( new AddToUrlEvent( 'act=paste' . '&pid=' . ModelId::fromModel($parentModel)->getSerialized() . - '&after=' . ModelId::fromValues($basicDefinition->getDataProvider(), '0')->getSerialized() + '&after=' . ModelId::fromValues($dataProvider, '0')->getSerialized() ), ContaoEvents::BACKEND_ADD_TO_URL ); @@ -547,7 +635,7 @@ private function getHeaderPasteTopButton(ModelInterface $parentModel, Environmen '%s', $urlEvent->getUrl(), StringUtil::specialchars($this->translate('pasteafter.0', $definition->getName())), - $imageEvent->getHtml() + $imageEvent->getHtml() ?? '' ); } @@ -565,7 +653,7 @@ private function getHeaderPasteTopButton(ModelInterface $parentModel, Environmen } /** - * Obtain the id of the grand parent (if any). + * Obtain the id of the grand-parent (if any). * * @param ContainerInterface $parentDefinition The parent definition. * @param ModelInterface $parentModel The parent model. @@ -578,11 +666,14 @@ private function getGrandParentId( ModelInterface $parentModel, EnvironmentInterface $environment ) { - if ('' === ($grandParentName = $parentDefinition->getBasicDefinition()->getParentDataProvider())) { + if (null === ($grandParentName = $parentDefinition->getBasicDefinition()->getParentDataProvider())) { return null; } - $relationship = $environment->getDataDefinition()->getModelRelationshipDefinition()->getChildCondition( + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $relationship = $definition->getModelRelationshipDefinition()->getChildCondition( $grandParentName, $parentDefinition->getName() ); @@ -592,15 +683,21 @@ private function getGrandParentId( } $grandParentProvider = $environment->getDataProvider($grandParentName); + assert($grandParentProvider instanceof DataProviderInterface); $config = $grandParentProvider->getEmptyConfig(); $config->setFilter((array) $relationship->getInverseFilterFor($parentModel)); $parents = $grandParentProvider->fetchAll($config); + assert($parents instanceof CollectionInterface); + + $firstModel = $parents->get(0); + assert($firstModel instanceof ModelInterface); if (1 === $parents->length()) { - return ModelId::fromModel($parents->get(0))->getSerialized(); + return ModelId::fromModel($firstModel)->getSerialized(); } + return false; } } diff --git a/src/Contao/View/Contao2BackendView/ActionHandler/PasteHandler.php b/src/Contao/View/Contao2BackendView/ActionHandler/PasteHandler.php index a94d2c23..62141f14 100644 --- a/src/Contao/View/Contao2BackendView/ActionHandler/PasteHandler.php +++ b/src/Contao/View/Contao2BackendView/ActionHandler/PasteHandler.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Sven Baumann * @author David Molineus - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -26,7 +27,10 @@ use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminator; use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminatorAwareTrait; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\ViewHelpers; +use ContaoCommunityAlliance\DcGeneral\Controller\ControllerInterface; use ContaoCommunityAlliance\DcGeneral\Data\ModelId; +use ContaoCommunityAlliance\DcGeneral\Data\ModelIdInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\BasicDefinitionInterface; use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; use ContaoCommunityAlliance\DcGeneral\Event\ActionEvent; @@ -74,7 +78,7 @@ public function handleEvent(ActionEvent $event) return; } - if (false !== ($response = $this->process($event->getEnvironment()))) { + if (null !== ($response = $this->process($event->getEnvironment()))) { $event->setResponse($response); } } @@ -88,20 +92,31 @@ public function handleEvent(ActionEvent $event) */ protected function process(EnvironmentInterface $environment) { - $input = $environment->getInputProvider(); - $clipboard = $environment->getClipboard(); - $definition = $environment->getDataDefinition()->getBasicDefinition(); + $input = $environment->getInputProvider(); + assert($input instanceof InputProviderInterface); + + $clipboard = $environment->getClipboard(); + assert($clipboard instanceof ClipboardInterface); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $basicDefinition = $definition->getBasicDefinition(); + assert($basicDefinition instanceof BasicDefinitionInterface); // Tree mode needs special handling. - if ($this->needTreeModeShowAll($definition, $input)) { + if ($this->needTreeModeShowAll($basicDefinition, $input)) { return $this->callAction($environment, 'showAll'); } + $providerName = $basicDefinition->getDataProvider(); + assert(\is_string($providerName)); + // Check if it is a simple create-paste of a single model, if so, redirect to edit view. if ( $this->isSimpleCreatePaste( $clipboard, - $environment->getDataDefinition()->getBasicDefinition()->getDataProvider() + $providerName ) ) { return $this->callAction($environment, 'create'); @@ -113,11 +128,15 @@ protected function process(EnvironmentInterface $environment) $parentModelId = $this->modelIdFromParameter($input, 'pid'); $items = []; - $environment->getController()->applyClipboardActions($source, $after, $into, $parentModelId, null, $items); + $controller = $environment->getController(); + assert($controller instanceof ControllerInterface); + + $controller->applyClipboardActions($source, $after, $into, $parentModelId, null, $items); foreach ($items as $item) { $clipboard->remove($item); } + $clipboard->saveTo($environment); // If we use paste all handler don´t redirect yet. @@ -139,7 +158,10 @@ protected function process(EnvironmentInterface $environment) */ private function checkPermission(ActionEvent $event) { - if (true === $event->getEnvironment()->getDataDefinition()->getBasicDefinition()->isEditable()) { + $definition = $event->getEnvironment()->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + if (true === $definition->getBasicDefinition()->isEditable()) { return true; } @@ -200,7 +222,7 @@ private function isSimpleCreatePaste(ClipboardInterface $clipboard, $provider) * @param InputProviderInterface $input The input provider. * @param string $name The parameter to retrieve. * - * @return ModelId|null + * @return ModelIdInterface|null */ private function modelIdFromParameter(InputProviderInterface $input, $name) { diff --git a/src/Contao/View/Contao2BackendView/ActionHandler/SelectHandler.php b/src/Contao/View/Contao2BackendView/ActionHandler/SelectHandler.php index 27de31fa..79635779 100644 --- a/src/Contao/View/Contao2BackendView/ActionHandler/SelectHandler.php +++ b/src/Contao/View/Contao2BackendView/ActionHandler/SelectHandler.php @@ -23,9 +23,11 @@ namespace ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\ActionHandler; +use ArrayObject; use ContaoCommunityAlliance\Contao\Bindings\ContaoEvents; use ContaoCommunityAlliance\Contao\Bindings\Events\System\GetReferrerEvent; use ContaoCommunityAlliance\DcGeneral\Action; +use ContaoCommunityAlliance\DcGeneral\Clipboard\ClipboardInterface; use ContaoCommunityAlliance\DcGeneral\Clipboard\Filter; use ContaoCommunityAlliance\DcGeneral\Contao\DataDefinition\Definition\Contao2BackendViewDefinitionInterface; use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminator; @@ -33,14 +35,38 @@ use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\PrepareMultipleModelsActionEvent; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\ViewHelpers; use ContaoCommunityAlliance\DcGeneral\Data\CollectionInterface; +use ContaoCommunityAlliance\DcGeneral\Data\DataProviderInterface; use ContaoCommunityAlliance\DcGeneral\Data\ModelId; +use ContaoCommunityAlliance\DcGeneral\Data\ModelIdInterface; use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\BasicDefinitionInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\Command; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\CommandCollectionInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\PaletteInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\PropertyInterface; use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; use ContaoCommunityAlliance\DcGeneral\Event\ActionEvent; +use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; +use ContaoCommunityAlliance\DcGeneral\SessionStorageInterface; use ContaoCommunityAlliance\DcGeneral\View\ActionHandler\CallActionTrait; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; + +use function array_filter; +use function array_intersect; +use function array_intersect_key; +use function array_map; +use function array_unique; +use function assert; +use function count; +use function in_array; +use function is_array; +use function is_string; +use function method_exists; +use function serialize; +use function sprintf; +use function ucfirst; +use function unserialize; /** * Class SelectController. @@ -84,12 +110,12 @@ public function handleEvent(ActionEvent $event) return; } - if (false !== ($response = $this->process($action, $event->getEnvironment()))) { + if (null !== ($response = $this->process($action, $event->getEnvironment()))) { $event->setResponse($response); // Stop the event here. // Don´t allow any listener for manipulation here. - // Use the sub events their are called. + // Use the sub events there are called. $event->stopPropagation(); } } @@ -100,16 +126,16 @@ public function handleEvent(ActionEvent $event) * @param Action $action The action. * @param EnvironmentInterface $environment The environment. * - * @return string + * @return string|null */ private function process(Action $action, EnvironmentInterface $environment) { - $actionMethod = \sprintf( + $actionMethod = sprintf( 'handle%sAllAction', - \ucfirst($this->getSubmitAction($environment, $this->regardSelectMode($environment))) + ucfirst($this->getSubmitAction($environment, $this->regardSelectMode($environment))) ); - if (false !== ($response = $this->{$actionMethod}($environment, $action))) { + if (null !== ($response = $this->{$actionMethod}($environment, $action))) { return $response; } @@ -126,12 +152,15 @@ private function process(Action $action, EnvironmentInterface $environment) */ private function getSubmitAction(EnvironmentInterface $environment, $regardSelectMode = false) { + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + if ( !$regardSelectMode - && $environment->getInputProvider()->hasParameter('select') - && !$environment->getInputProvider()->hasValue('properties') + && $inputProvider->hasParameter('select') + && !$inputProvider->hasValue('properties') ) { - return 'select' . \ucfirst($environment->getInputProvider()->getParameter('select')); + return 'select' . ucfirst($inputProvider->getParameter('select')); } if (null !== ($action = $this->determineAction($environment))) { @@ -139,11 +168,11 @@ private function getSubmitAction(EnvironmentInterface $environment, $regardSelec } if ($regardSelectMode) { - return $environment->getInputProvider()->getParameter('mode') ?: null; + return $inputProvider->getParameter('mode') ?: ''; } - return $environment->getInputProvider()->getParameter('select') ? - 'select' . \ucfirst($environment->getInputProvider()->getParameter('select')) : null; + return $inputProvider->getParameter('select') ? + 'select' . ucfirst($inputProvider->getParameter('select')) : ''; } /** @@ -155,13 +184,16 @@ private function getSubmitAction(EnvironmentInterface $environment, $regardSelec */ private function determineAction(EnvironmentInterface $environment) { + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + foreach (['delete', 'cut', 'copy', 'override', 'edit'] as $action) { if ( - $environment->getInputProvider()->hasValue($action) - || $environment->getInputProvider()->hasValue($action . '_save') - || $environment->getInputProvider()->hasValue($action . '_saveNback') + $inputProvider->hasValue($action) + || $inputProvider->hasValue($action . '_save') + || $inputProvider->hasValue($action . '_saveNback') ) { - $environment->getInputProvider()->setParameter('mode', $action); + $inputProvider->setParameter('mode', $action); return $action; } @@ -180,9 +212,10 @@ private function determineAction(EnvironmentInterface $environment) private function regardSelectMode(EnvironmentInterface $environment) { $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); $regardSelectMode = false; - \array_map( + array_map( function ($value) use ($inputProvider, &$regardSelectMode) { if (!$inputProvider->hasValue($value)) { return false; @@ -211,28 +244,31 @@ function ($value) use ($inputProvider, &$regardSelectMode) { * @param Action $action The dcg action. * @param string $submitAction The submit action name. * - * @return ModelId[] + * @return list */ - private function getModelIds(EnvironmentInterface $environment, Action $action, $submitAction) + private function getModelIds(EnvironmentInterface $environment, Action $action, string $submitAction): array { - $valueKey = \in_array($submitAction, ['edit', 'override']) ? 'properties' : 'models'; - $modelIds = (array) $environment->getInputProvider()->getValue($valueKey); + $valueKey = in_array($submitAction, ['edit', 'override']) ? 'properties' : 'models'; - if (!empty($modelIds)) { - $modelIds = \array_map( - function ($modelId) { - return ModelId::fromSerialized($modelId); - }, - $modelIds - ); + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); - $event = new PrepareMultipleModelsActionEvent($environment, $action, $modelIds, $submitAction); - $environment->getEventDispatcher()->dispatch($event, $event::NAME); + $modelIds = array_values((array) $inputProvider->getValue($valueKey)); - $modelIds = $event->getModelIds(); + if (empty($modelIds)) { + return []; } + $modelIds = array_map( + static fn (string $modelId): ModelIdInterface => ModelId::fromSerialized($modelId), + $modelIds + ); + + $event = new PrepareMultipleModelsActionEvent($environment, $action, $modelIds, $submitAction); + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + $dispatcher->dispatch($event, $event::NAME); - return $modelIds; + return $event->getModelIds(); } /** @@ -251,11 +287,7 @@ private function handleSelectModelsAllAction(EnvironmentInterface $environment, $this->clearClipboard($environment); $this->handleGlobalCommands($environment); - if ($response = $this->callAction($environment, 'selectModelAll')) { - return $response; - } - - return null; + return $this->callAction($environment, 'selectModelAll'); } /** @@ -282,11 +314,7 @@ private function handleSelectPropertiesAllAction(EnvironmentInterface $environme $this->setIntersectProperties($collection, $environment); $this->setIntersectValues($collection, $environment); - if ($response = $this->callAction($environment, 'selectPropertyAll')) { - return $response; - } - - return null; + return $this->callAction($environment, 'selectPropertyAll'); } /** @@ -305,6 +333,8 @@ private function handleDeleteAllAction(EnvironmentInterface $environment, Action $this->handleGlobalCommands($environment); $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + foreach ($this->getModelIds($environment, $action, $this->getSubmitAction($environment)) as $modelId) { $inputProvider->setParameter('id', $modelId->getSerialized()); @@ -331,6 +361,8 @@ private function handleDeleteAllAction(EnvironmentInterface $environment, Action private function handleCutAllAction(EnvironmentInterface $environment, Action $action) { $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + foreach ($this->getModelIds($environment, $action, $this->getSubmitAction($environment)) as $modelId) { $inputProvider->setParameter('source', $modelId->getSerialized()); @@ -357,6 +389,8 @@ private function handleCutAllAction(EnvironmentInterface $environment, Action $a private function handleCopyAllAction(EnvironmentInterface $environment, Action $action) { $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + foreach ($this->getModelIds($environment, $action, $this->getSubmitAction($environment)) as $modelId) { $inputProvider->setParameter('source', $modelId->getSerialized()); @@ -376,7 +410,7 @@ private function handleCopyAllAction(EnvironmentInterface $environment, Action $ * @param EnvironmentInterface $environment The environment. * @param Action $action The action. * - * @return string + * @return string|null * * @SuppressWarnings(PHPMD.UnusedFormalParameter) * @SuppressWarnings(PHPMD.UnusedPrivateMethod) @@ -400,7 +434,7 @@ private function handleEditAllAction(EnvironmentInterface $environment, Action $ * @param EnvironmentInterface $environment The environment. * @param Action $action The action. * - * @return string + * @return string|null * * @SuppressWarnings(PHPMD.UnusedFormalParameter) * @SuppressWarnings(PHPMD.UnusedPrivateMethod) @@ -428,11 +462,17 @@ private function handleOverrideAllAction(EnvironmentInterface $environment, Acti private function handleGlobalCommands(EnvironmentInterface $environment) { $dataDefinition = $environment->getDataDefinition(); - $backendView = $dataDefinition->getDefinition(Contao2BackendViewDefinitionInterface::NAME); + assert($dataDefinition instanceof ContainerInterface); + + $backendView = $dataDefinition->getDefinition(Contao2BackendViewDefinitionInterface::NAME); + assert($backendView instanceof Contao2BackendViewDefinitionInterface); + + $globalCommands = $backendView->getGlobalCommands(); + assert($globalCommands instanceof CommandCollectionInterface); $backButton = null; - if ($backendView->getGlobalCommands()->hasCommandNamed('back_button')) { - $backButton = $backendView->getGlobalCommands()->getCommandNamed('back_button'); + if ($globalCommands->hasCommandNamed('back_button')) { + $backButton = $globalCommands->getCommandNamed('back_button'); } if (!$backButton) { @@ -441,7 +481,7 @@ private function handleGlobalCommands(EnvironmentInterface $environment) $parametersBackButton = $backButton->getParameters(); - if (\in_array($this->getSelectAction($environment), ['properties', 'edit'])) { + if (in_array($this->getSelectAction($environment), ['properties', 'edit'])) { $parametersBackButton->offsetSet('act', 'select'); $parametersBackButton->offsetSet( 'select', @@ -464,8 +504,8 @@ private function handleGlobalCommands(EnvironmentInterface $environment) ->setName('close_all_button') ->setLabel('MSC.closeAll.0') ->setDescription('MSC.closeAll.1') - ->setParameters(new \ArrayObject()) - ->setExtra(new \ArrayObject($closeExtra)) + ->setParameters(new ArrayObject()) + ->setExtra(new ArrayObject($closeExtra)) ->setDisabled(false); } @@ -478,7 +518,10 @@ private function handleGlobalCommands(EnvironmentInterface $environment) */ private function getSelectAction(EnvironmentInterface $environment) { - return $environment->getInputProvider()->getParameter('select'); + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + + return $inputProvider->getParameter('select'); } /** @@ -490,14 +533,21 @@ private function getSelectAction(EnvironmentInterface $environment) */ private function getReferrerUrl(EnvironmentInterface $environment) { + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $parentDefinition = $environment->getParentDataDefinition(); $event = new GetReferrerEvent( true, - (null !== $environment->getParentDataDefinition()) - ? $environment->getParentDataDefinition()->getName() - : $environment->getDataDefinition()->getName() + (null !== $parentDefinition) + ? $parentDefinition->getName() + : $definition->getName() ); - $environment->getEventDispatcher()->dispatch($event, ContaoEvents::SYSTEM_GET_REFERRER); + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $dispatcher->dispatch($event, ContaoEvents::SYSTEM_GET_REFERRER); return $event->getReferrerUrl(); } @@ -511,20 +561,29 @@ private function getReferrerUrl(EnvironmentInterface $environment) */ private function clearClipboard(EnvironmentInterface $environment) { - $basicDefinition = $environment->getDataDefinition()->getBasicDefinition(); + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $basicDefinition = $definition->getBasicDefinition(); + assert($basicDefinition instanceof BasicDefinitionInterface); $filter = new Filter(); - $filter->andModelIsFromProvider($basicDefinition->getDataProvider()); - if ($basicDefinition->getParentDataProvider()) { - $filter->andParentIsFromProvider($basicDefinition->getParentDataProvider()); + + $dataProvider = $basicDefinition->getDataProvider(); + assert(is_string($dataProvider)); + + $filter->andModelIsFromProvider($dataProvider); + if (null !== ($parentProvider = $basicDefinition->getParentDataProvider())) { + $filter->andParentIsFromProvider($parentProvider); } else { $filter->andHasNoParent(); } $clipboard = $environment->getClipboard(); + assert($clipboard instanceof ClipboardInterface); $items = $clipboard->fetch($filter); - if (\count($items) < 1) { + if (count($items) < 1) { return; } @@ -542,9 +601,15 @@ private function clearClipboard(EnvironmentInterface $environment) */ private function getSelectCollection(EnvironmentInterface $environment) { - $sessionStorage = $environment->getSessionStorage(); $dataDefinition = $environment->getDataDefinition(); - $dataProvider = $environment->getDataProvider(); + assert($dataDefinition instanceof ContainerInterface); + + $sessionStorage = $environment->getSessionStorage(); + assert($sessionStorage instanceof SessionStorageInterface); + + $dataProvider = $environment->getDataProvider(); + assert($dataProvider instanceof DataProviderInterface); + $session = $sessionStorage->get($dataDefinition->getName() . '.' . $this->getSubmitAction($environment, true)); @@ -553,8 +618,8 @@ private function getSelectCollection(EnvironmentInterface $environment) $modelIds[] = ModelId::fromSerialized($modelId)->getId(); } - $idProperty = \method_exists($dataProvider, 'getIdProperty') ? $dataProvider->getIdProperty() : 'id'; - return $dataProvider->fetchAll( + $idProperty = method_exists($dataProvider, 'getIdProperty') ? $dataProvider->getIdProperty() : 'id'; + $collection = $dataProvider->fetchAll( $dataProvider->getEmptyConfig()->setFilter( [ [ @@ -565,6 +630,8 @@ private function getSelectCollection(EnvironmentInterface $environment) ] ) ); + assert($collection instanceof CollectionInterface); + return $collection; } /** @@ -577,8 +644,11 @@ private function getSelectCollection(EnvironmentInterface $environment) */ private function setIntersectProperties(CollectionInterface $collection, EnvironmentInterface $environment) { - $sessionStorage = $environment->getSessionStorage(); $dataDefinition = $environment->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + + $sessionStorage = $environment->getSessionStorage(); + assert($sessionStorage instanceof SessionStorageInterface); $session = $sessionStorage->get($dataDefinition->getName() . '.' . $this->getSubmitAction($environment, true)); @@ -597,12 +667,15 @@ private function setIntersectProperties(CollectionInterface $collection, Environ */ private function setIntersectValues(CollectionInterface $collection, EnvironmentInterface $environment) { - $sessionStorage = $environment->getSessionStorage(); $dataDefinition = $environment->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + + $sessionStorage = $environment->getSessionStorage(); + assert($sessionStorage instanceof SessionStorageInterface); $session = $sessionStorage->get($dataDefinition->getName() . '.' . $this->getSubmitAction($environment, true)); - if (!$session['intersectProperties'] || !\count($session['intersectProperties'])) { + if (!$session['intersectProperties'] || !count($session['intersectProperties'])) { return; } @@ -620,7 +693,10 @@ private function setIntersectValues(CollectionInterface $collection, Environment */ private function collectIntersectModelProperties(CollectionInterface $collection, EnvironmentInterface $environment) { - $palettesDefinition = $environment->getDataDefinition()->getPalettesDefinition(); + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $palettesDefinition = $definition->getPalettesDefinition(); $properties = []; foreach ($collection->getIterator() as $model) { @@ -636,7 +712,13 @@ private function collectIntersectModelProperties(CollectionInterface $collection } } - return \array_filter( + // We always have to keep the id in the array. + $dataProvider = $environment->getDataProvider(); + assert($dataProvider instanceof DataProviderInterface); + $idProperty = method_exists($dataProvider, 'getIdProperty') ? $dataProvider->getIdProperty() : 'id'; + $properties[$idProperty] = $collection->count(); + + return array_filter( $properties, function ($count) use ($collection) { return $count === $collection->count(); @@ -654,29 +736,22 @@ function ($count) use ($collection) { */ private function collectIntersectValues(CollectionInterface $collection, EnvironmentInterface $environment) { - $sessionStorage = $environment->getSessionStorage(); $dataDefinition = $environment->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + + $sessionStorage = $environment->getSessionStorage(); + assert($sessionStorage instanceof SessionStorageInterface); $session = $sessionStorage->get($dataDefinition->getName() . '.' . $this->getSubmitAction($environment, true)); $values = []; foreach ($collection->getIterator() as $model) { - $modelValues = \array_intersect_key($model->getPropertiesAsArray(), $session['intersectProperties']); + $modelValues = array_intersect_key($model->getPropertiesAsArray(), $session['intersectProperties']); foreach ($modelValues as $modelProperty => $modelValue) { - if (1 === $collection->count()) { - $values[$modelProperty] = $modelValue; - - continue; - } - $values[$modelProperty][] = $modelValue; } } - if (1 === $collection->count()) { - return $values; - } - $intersectValues = []; foreach ($values as $propertyName => $propertyValues) { if (!($value = $this->getUniqueValueFromArray($propertyValues))) { @@ -699,14 +774,14 @@ private function collectIntersectValues(CollectionInterface $collection, Environ */ private function getVisibleAndEditAbleProperties(PaletteInterface $palette, ModelInterface $model) { - return \array_intersect( - \array_map( + return array_intersect( + array_map( function (PropertyInterface $property) { return $property->getName(); }, $palette->getVisibleProperties($model) ), - \array_map( + array_map( function (PropertyInterface $property) { return $property->getName(); }, @@ -726,63 +801,63 @@ private function getUniqueValueFromArray(array $values) { $serializedValues = false; foreach ($values as $key => $value) { - if (!\is_array($value)) { + if (!is_array($value)) { continue; } - $values[$key] = \serialize($value); + $values[$key] = serialize($value); $serializedValues = true; } if (!$serializedValues) { - return 1 === \count(\array_unique($values)) ? $values[0] : null; + return 1 === count(array_unique($values)) ? $values[0] : null; } - return 1 === \count(\array_unique($values)) ? \unserialize($values[0], ['allowed_classes' => true]) : null; + return 1 === count(array_unique($values)) ? unserialize($values[0], ['allowed_classes' => true]) : null; } /** * Handle session data for override/edit all. * - * @param array $collection The collection. - * @param string $index The session index for the collection. - * @param EnvironmentInterface $environment The environment. + * @param list $collection The collection. + * @param 'models'|'properties' $index The session index for the collection. + * @param EnvironmentInterface $environment The environment. * - * @return array The collection. + * @return list< + * ModelIdInterface> The collection. */ - private function handleSessionOverrideEditAll(array $collection, $index, EnvironmentInterface $environment) + private function handleSessionOverrideEditAll(array $collection, string $index, EnvironmentInterface $environment) { $dataDefinition = $environment->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + $sessionStorage = $environment->getSessionStorage(); + assert($sessionStorage instanceof SessionStorageInterface); - $session = []; - if ($sessionStorage->has($dataDefinition->getName() . '.' . $this->getSubmitAction($environment, true))) { - $session = - $sessionStorage->get($dataDefinition->getName() . '.' . $this->getSubmitAction($environment, true)); + /** @var array{ + * models: list, + * intersectProperties: array, + * intersectValues: array, + * properties?: list, + * editProperties?: list, + * } $session */ + $session = ['models' => [], 'intersectProperties' => [], 'intersectValues' => []]; + $sessionKey = $dataDefinition->getName() . '.' . $this->getSubmitAction($environment, true); + if ($sessionStorage->has($sessionKey)) { + $session = $sessionStorage->get($sessionKey); } // If collection not empty set to the session and return it. if (!empty($collection)) { - $sessionCollection = \array_map( - function ($item) use ($index) { - if (!\in_array($index, ['models', 'properties'])) { - return $item; - } - - if (!$item instanceof ModelId) { - $item = ModelId::fromSerialized($item); - } - - return $item->getSerialized(); - }, + $sessionCollection = array_map( + static fn (ModelIdInterface $item): string => $item->getSerialized(), $collection ); $session[$index] = $sessionCollection; - $sessionStorage - ->set($dataDefinition->getName() . '.' . $this->getSubmitAction($environment, true), $session); + $sessionStorage->set($sessionKey, $session); return $collection; } @@ -793,17 +868,9 @@ function ($item) use ($index) { } // Get the verify collection from the session and return it. - $collection = \array_map( - function ($item) use ($index) { - if (!\in_array($index, ['models', 'properties'])) { - return $item; - } - - return ModelId::fromSerialized($item); - }, - $session[$index] + return array_map( + static fn (string $item): ModelIdInterface => ModelId::fromSerialized($item), + array_values($session[$index]) ); - - return $collection; } } diff --git a/src/Contao/View/Contao2BackendView/ActionHandler/ShowHandler.php b/src/Contao/View/Contao2BackendView/ActionHandler/ShowHandler.php index 7f6f9ee9..be65435a 100644 --- a/src/Contao/View/Contao2BackendView/ActionHandler/ShowHandler.php +++ b/src/Contao/View/Contao2BackendView/ActionHandler/ShowHandler.php @@ -31,16 +31,23 @@ use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminatorAwareTrait; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\ContaoBackendViewTemplate; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\ViewHelpers; +use ContaoCommunityAlliance\DcGeneral\Controller\ControllerInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\PropertiesDefinitionInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\Properties\PropertyInterface; +use ContaoCommunityAlliance\DcGeneral\Data\DataProviderInterface; use ContaoCommunityAlliance\DcGeneral\Data\ModelId; use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; use ContaoCommunityAlliance\DcGeneral\Data\MultiLanguageDataProviderInterface; use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; use ContaoCommunityAlliance\DcGeneral\Event\ActionEvent; use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralRuntimeException; +use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; +use ContaoCommunityAlliance\DcGeneral\View\ViewInterface; use ContaoCommunityAlliance\Translator\TranslatorInterface; use Contao\StringUtil; use LogicException; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; use function array_merge; use function array_values; @@ -87,22 +94,31 @@ public function handleEvent(ActionEvent $event) */ protected function getModel(EnvironmentInterface $environment) { - $modelId = ModelId::fromSerialized($environment->getInputProvider()->getParameter('id')); + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + + $modelId = ModelId::fromSerialized($inputProvider->getParameter('id')); $dataProvider = $environment->getDataProvider($modelId->getDataProviderName()); - $model = $dataProvider->fetch($dataProvider->getEmptyConfig()->setId($modelId->getId())); + assert($dataProvider instanceof DataProviderInterface); + + $model = $dataProvider->fetch($dataProvider->getEmptyConfig()->setId($modelId->getId())); if ($model) { return $model; } $eventDispatcher = $environment->getEventDispatcher(); + assert($eventDispatcher instanceof EventDispatcherInterface); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); $eventDispatcher->dispatch( new LogEvent( sprintf( 'Could not find ID %s in %s. DC_General show()', $modelId->getId(), - $environment->getDataDefinition()->getName() + $definition->getName() ), __CLASS__ . '::' . __FUNCTION__, 'ERROR' @@ -126,17 +142,20 @@ protected function getModel(EnvironmentInterface $environment) protected function getPropertyLabel(EnvironmentInterface $environment, PropertyInterface $property) { $translator = $environment->getTranslator(); - $key = $property->getLabel(); + assert($translator instanceof TranslatorInterface); + + $key = $property->getLabel(); - if (null === $key) { + if ('' === $key) { throw new LogicException('Missing label for property ' . $property->getName()); } - if ('' !== $key) { - $label = $translator->translate($key, $environment->getDataDefinition()->getName()); - if ($label !== $key) { - return $label; - } + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $label = $translator->translate($key, $definition->getName()); + if ($label !== $key) { + return $label; } $mscKey = 'MSC.' . $property->getName() . '.0'; @@ -161,7 +180,11 @@ protected function getPropertyLabel(EnvironmentInterface $environment, PropertyI protected function convertModel(ModelInterface $model, EnvironmentInterface $environment): array { $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + $properties = $definition->getPropertiesDefinition(); + assert($properties instanceof PropertiesDefinitionInterface); + $palette = $definition->getPalettesDefinition()->findPalette($model); $values = [ 'system' => [], @@ -175,9 +198,10 @@ protected function convertModel(ModelInterface $model, EnvironmentInterface $env // Add only visible properties. foreach ($palette->getVisibleProperties($model) as $paletteProperty) { $palettePropertyName = $paletteProperty->getName(); - if (!($visibleProperty = $properties->getProperty($palettePropertyName))) { - throw new DcGeneralRuntimeException('Unable to retrieve property ' . $paletteProperty->getName()); + if (!$properties->hasProperty($palettePropertyName)) { + throw new DcGeneralRuntimeException('Unable to retrieve property ' . $palettePropertyName); } + $visibleProperty = $properties->getProperty($palettePropertyName); // Make it human-readable. $values['visible'][$palettePropertyName] = ViewHelpers::getReadableFieldValue( @@ -196,14 +220,9 @@ protected function convertModel(ModelInterface $model, EnvironmentInterface $env if (isset($values['visible'][$propertyName])) { continue; } - - if (!($systemProperty = $properties->getProperty($propertyName))) { - throw new DcGeneralRuntimeException('Unable to retrieve property ' . $propertyName); - } - $values['system'][$propertyName] = $model->getProperty($propertyName); $labels['system'][$propertyName] = - sprintf('%s [%s]', $this->getPropertyLabel($environment, $systemProperty), $propertyName); + sprintf('%s [%s]', $this->getPropertyLabel($environment, $property), $propertyName); } return [ @@ -249,24 +268,41 @@ protected function getHeadline(TranslatorInterface $translator, $model) */ protected function process(Action $action, EnvironmentInterface $environment) { - if ($environment->getDataDefinition()->getBasicDefinition()->isEditOnlyMode()) { - return $environment->getView()->edit($action); + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $view = $environment->getView(); + assert($view instanceof ViewInterface); + + if ($definition->getBasicDefinition()->isEditOnlyMode()) { + return $view->edit($action); } - $modelId = ModelId::fromSerialized($environment->getInputProvider()->getParameter('id')); + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + + $modelId = ModelId::fromSerialized($inputProvider->getParameter('id')); $dataProvider = $environment->getDataProvider($modelId->getDataProviderName()); - $translator = $environment->getTranslator(); - $model = $this->getModel($environment); - $data = $this->convertModel($model, $environment); + + $translator = $environment->getTranslator(); + assert($translator instanceof TranslatorInterface); + + $model = $this->getModel($environment); + assert($model instanceof ModelInterface); + + $data = $this->convertModel($model, $environment); $template = (new ContaoBackendViewTemplate('dcbe_general_show')) ->set('headline', $this->getHeadline($translator, $model)) ->set('arrFields', $data['values']) ->set('arrLabels', $data['labels']); + $controller = $environment->getController(); + assert($controller instanceof ControllerInterface); + if ($dataProvider instanceof MultiLanguageDataProviderInterface) { $template - ->set('languages', $environment->getController()->getSupportedLanguages($model->getId())) + ->set('languages', $controller->getSupportedLanguages($model->getId())) ->set('currentLanguage', $dataProvider->getCurrentLanguage()) ->set('languageSubmit', $translator->translate('MSC.showSelected')) ->set('backBT', StringUtil::specialchars($translator->translate('MSC.backBT'))); diff --git a/src/Contao/View/Contao2BackendView/ActionHandler/ToggleHandler.php b/src/Contao/View/Contao2BackendView/ActionHandler/ToggleHandler.php index 1dda36f1..6d497bfd 100644 --- a/src/Contao/View/Contao2BackendView/ActionHandler/ToggleHandler.php +++ b/src/Contao/View/Contao2BackendView/ActionHandler/ToggleHandler.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2021 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -16,7 +16,7 @@ * @author David Molineus * @author Benedict Zinke * @author Ingolf Steinhardt - * @copyright 2013-2021 Contao Community Alliance. + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -27,9 +27,13 @@ use ContaoCommunityAlliance\DcGeneral\Contao\DataDefinition\Definition\Contao2BackendViewDefinitionInterface; use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminator; use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminatorAwareTrait; +use ContaoCommunityAlliance\DcGeneral\Data\ConfigInterface; +use ContaoCommunityAlliance\DcGeneral\Data\DataProviderInterface; use ContaoCommunityAlliance\DcGeneral\Data\ModelId; use ContaoCommunityAlliance\DcGeneral\Data\ModelIdInterface; +use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; use ContaoCommunityAlliance\DcGeneral\Data\MultiLanguageDataProviderInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\CommandInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\ToggleCommandInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\TranslatedToggleCommandInterface; @@ -38,6 +42,7 @@ use ContaoCommunityAlliance\DcGeneral\Event\PostPersistModelEvent; use ContaoCommunityAlliance\DcGeneral\Event\PrePersistModelEvent; use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; /** * This class handles toggle commands. @@ -110,7 +115,12 @@ protected function process( ModelIdInterface $modelId = null ) { $dataProvider = $environment->getDataProvider(); - $newState = $this->determineNewState($environment->getInputProvider(), $operation->isInverse()); + assert($dataProvider instanceof DataProviderInterface); + + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + + $newState = $this->determineNewState($inputProvider, $operation->isInverse()); // Override the language for language aware toggling. if ( @@ -122,11 +132,21 @@ protected function process( $dataProvider->setCurrentLanguage($operation->getLanguage()); } - $model = $dataProvider->fetch($dataProvider->getEmptyConfig()->setId($modelId->getId())); + $provider = $dataProvider->getEmptyConfig(); + assert($provider instanceof ConfigInterface); + assert($modelId instanceof ModelIdInterface); + $config = $provider->setId($modelId->getId()); + assert($config instanceof ConfigInterface); + + $model = $dataProvider->fetch($config); + assert($model instanceof ModelInterface); + $originalModel = clone $model; $originalModel->setId($model->getId()); $model->setProperty($operation->getToggleProperty(), $newState); + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); $dispatcher->dispatch( new PrePersistModelEvent($environment, $model, $originalModel), @@ -142,6 +162,7 @@ protected function process( // Select the previous language. if (isset($language)) { + /** @var MultiLanguageDataProviderInterface $dataProvider */ $dataProvider->setCurrentLanguage($language); } } @@ -159,16 +180,22 @@ private function checkPermission(ActionEvent $event, ToggleCommandInterface $com { $environment = $event->getEnvironment(); - if (true === $environment->getDataDefinition()->getBasicDefinition()->isEditable() && !$command->isDisabled()) { + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + if (true === !$command->isDisabled() && $definition->getBasicDefinition()->isEditable()) { return true; } + $operation = $this->getOperation($event->getAction(), $environment); + assert($operation instanceof ToggleCommandInterface); + $event->setResponse( \sprintf( '
You have no permission for toggle %s.
', - $this->getOperation($event->getAction(), $environment)->getToggleProperty() + $operation->getToggleProperty() ) ); @@ -185,12 +212,16 @@ private function checkPermission(ActionEvent $event, ToggleCommandInterface $com private function getModelId(EnvironmentInterface $environment) { $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); if ($inputProvider->hasParameter('id') && $inputProvider->getParameter('id')) { $modelId = ModelId::fromSerialized($inputProvider->getParameter('id')); } - if (!(isset($modelId) && ($environment->getDataDefinition()->getName() === $modelId->getDataProviderName()))) { + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + if (!(isset($modelId) && ($definition->getName() === $modelId->getDataProviderName()))) { return null; } @@ -203,14 +234,18 @@ private function getModelId(EnvironmentInterface $environment) * @param Action $action The action. * @param EnvironmentInterface $environment The environment. * - * @return CommandInterface + * @return CommandInterface|null */ private function getOperation(Action $action, EnvironmentInterface $environment) { - /** @var Contao2BackendViewDefinitionInterface $definition */ - $definition = $environment->getDataDefinition()->getDefinition(Contao2BackendViewDefinitionInterface::NAME); - $name = $action->getName(); - $commands = $definition->getModelCommands(); + $dataDefinition = $environment->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + + $definition = $dataDefinition->getDefinition(Contao2BackendViewDefinitionInterface::NAME); + assert($definition instanceof Contao2BackendViewDefinitionInterface); + + $name = $action->getName(); + $commands = $definition->getModelCommands(); if (!$commands->hasCommandNamed($name)) { return null; diff --git a/src/Contao/View/Contao2BackendView/BaseView.php b/src/Contao/View/Contao2BackendView/BaseView.php index 332d4bb0..ca5ea06c 100644 --- a/src/Contao/View/Contao2BackendView/BaseView.php +++ b/src/Contao/View/Contao2BackendView/BaseView.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2021 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -18,7 +18,8 @@ * @author David Molineus * @author Martin Treml * @author Sven Baumann - * @copyright 2013-2021 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -36,6 +37,8 @@ use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\GetGroupHeaderEvent; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\GetSelectModeButtonsEvent; use ContaoCommunityAlliance\DcGeneral\Controller\Ajax3X; +use ContaoCommunityAlliance\DcGeneral\Controller\ControllerInterface; +use ContaoCommunityAlliance\DcGeneral\Data\DataProviderInterface; use ContaoCommunityAlliance\DcGeneral\Data\ModelId; use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; @@ -44,13 +47,17 @@ use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; use ContaoCommunityAlliance\DcGeneral\Event\ActionEvent; use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralRuntimeException; +use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; use ContaoCommunityAlliance\DcGeneral\Panel\PanelContainerInterface; +use ContaoCommunityAlliance\DcGeneral\SessionStorageInterface; +use ContaoCommunityAlliance\Translator\TranslatorInterface; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\EventDispatcher\EventSubscriberInterface; /** * Class BaseView. * - * This class is the base class for the different backend view mode sub classes. + * This class is the base class for the different backend view mode sub-classes. * * @SuppressWarnings(PHPMD.TooManyPublicMethods) * @SuppressWarnings(PHPMD.CouplingBetweenObjects) @@ -118,10 +125,22 @@ public function handleAction(ActionEvent $event) { $GLOBALS['TL_CSS']['cca.dc-general.generalDriver'] = 'bundles/ccadcgeneral/css/generalDriver.css'; + $environment = $event->getEnvironment(); + assert($environment instanceof EnvironmentInterface); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $environment2 = $this->getEnvironment(); + assert($environment2 instanceof EnvironmentInterface); + + $definition2 = $environment2->getDataDefinition(); + assert($definition2 instanceof ContainerInterface); + if ( (null !== $event->getResponse()) - || $event->getEnvironment()->getDataDefinition()->getName() - !== $this->environment->getDataDefinition()->getName() + || $definition->getName() + !== $definition2->getName() ) { return; } @@ -139,7 +158,7 @@ public function handleAction(ActionEvent $event) if ('showAll' === $name) { $response = \call_user_func_array( [$this, $name], - \array_merge([$action], $action->getArguments()) + \array_values(\array_merge([$action], $action->getArguments())) ); $event->setResponse($response); @@ -172,12 +191,15 @@ public function handleAction(ActionEvent $event) */ public function setEnvironment(EnvironmentInterface $environment) { - if ($this->getEnvironment()) { - $this->environment->getEventDispatcher()->removeSubscriber($this); - } + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $dispatcher->removeSubscriber($this); $this->environment = $environment; - $this->getEnvironment()->getEventDispatcher()->addSubscriber($this); + $dispatcher->addSubscriber($this); + + return $this; } /** @@ -195,7 +217,13 @@ public function getEnvironment() */ protected function getDataDefinition() { - return $this->getEnvironment()->getDataDefinition(); + $environment = $this->getEnvironment(); + assert($environment instanceof EnvironmentInterface); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + return $definition; } /** @@ -208,7 +236,13 @@ protected function getDataDefinition() */ protected function translate($path, $section = null) { - return $this->getEnvironment()->getTranslator()->translate($path, $section); + $environment = $this->getEnvironment(); + assert($environment instanceof EnvironmentInterface); + + $translator = $environment->getTranslator(); + assert($translator instanceof TranslatorInterface); + + return $translator->translate($path, $section); } /** @@ -220,7 +254,12 @@ protected function translate($path, $section = null) */ protected function translateFallback($path) { - $translator = $this->getEnvironment()->getTranslator(); + $environment = $this->getEnvironment(); + assert($environment instanceof EnvironmentInterface); + + $translator = $environment->getTranslator(); + assert($translator instanceof TranslatorInterface); + // Try via definition name as domain first. $value = $translator->translate($path, $this->getDataDefinition()->getName()); if ($value !== $path) { @@ -271,7 +310,16 @@ public function getPanel() */ protected function getViewSection() { - return $this->getDataDefinition()->getDefinition(Contao2BackendViewDefinitionInterface::NAME); + $environment = $this->getEnvironment(); + assert($environment instanceof EnvironmentInterface); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $backendView = $definition->getDefinition(Contao2BackendViewDefinitionInterface::NAME); + assert($backendView instanceof Contao2BackendViewDefinitionInterface); + + return $backendView; } /** @@ -281,7 +329,13 @@ protected function getViewSection() */ protected function isSelectModeActive() { - return 'select' === $this->getEnvironment()->getInputProvider()->getParameter('act'); + $environment = $this->getEnvironment(); + assert($environment instanceof EnvironmentInterface); + + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + + return 'select' === $inputProvider->getParameter('act'); } /** @@ -293,20 +347,26 @@ protected function isSelectModeActive() * @param int $groupLength The length of the value to use for grouping (only used when grouping mode is * ListingConfigInterface::GROUP_CHAR). * - * @return string + * @return string|null */ public function formatCurrentValue($field, ModelInterface $model, $groupMode, $groupLength) { $environment = $this->getEnvironment(); - $property = $environment->getDataDefinition()->getPropertiesDefinition()->getProperty($field); + assert($environment instanceof EnvironmentInterface); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); // No property? Get out! - if (!$property) { + if (!$definition->getPropertiesDefinition()->hasProperty($field)) { return '-'; } $event = new GetGroupHeaderEvent($environment, $model, $field, null, $groupMode, $groupLength); - $environment->getEventDispatcher()->dispatch($event, $event::NAME); + + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + $dispatcher->dispatch($event, $event::NAME); return $event->getValue(); } @@ -319,10 +379,14 @@ public function formatCurrentValue($field, ModelInterface $model, $groupMode, $g protected function getSelectButtons() { $environment = $this->getEnvironment(); + assert($environment instanceof EnvironmentInterface); $event = new GetSelectModeButtonsEvent($environment); $event->setButtons([]); - $environment->getEventDispatcher()->dispatch($event, GetSelectModeButtonsEvent::NAME); + + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + $dispatcher->dispatch($event, GetSelectModeButtonsEvent::NAME); return $event->getButtons(); } @@ -336,7 +400,13 @@ protected function getSelectButtons() */ protected function isMultiLanguage($currentID) { - return (bool) \count($this->getEnvironment()->getController()->getSupportedLanguages($currentID)); + $environment = $this->getEnvironment(); + assert($environment instanceof EnvironmentInterface); + + $controller = $environment->getController(); + assert($controller instanceof ControllerInterface); + + return (bool) \count($controller->getSupportedLanguages($currentID)); } /** @@ -348,7 +418,13 @@ protected function isMultiLanguage($currentID) */ protected function getTemplate($name) { - return (new ContaoBackendViewTemplate($name))->setTranslator($this->getEnvironment()->getTranslator()); + $environment = $this->getEnvironment(); + assert($environment instanceof EnvironmentInterface); + + $translator = $environment->getTranslator(); + assert($translator instanceof TranslatorInterface); + + return (new ContaoBackendViewTemplate($name))->setTranslator($translator); } /** @@ -361,7 +437,10 @@ protected function getTemplate($name) public function handleAjaxCall() { $environment = $this->getEnvironment(); - $input = $environment->getInputProvider(); + assert($environment instanceof EnvironmentInterface); + + $input = $environment->getInputProvider(); + assert($input instanceof InputProviderInterface); if (true === ($input->hasParameter('id'))) { // Redefine the parameter id if this isn´t model id conform. @@ -371,7 +450,9 @@ public function handleAjaxCall() } $modelId = ModelId::fromSerialized($input->getParameter('id')); $dataProvider = $environment->getDataProvider($modelId->getDataProviderName()); - $model = $dataProvider->fetch($dataProvider->getEmptyConfig()->setId($modelId->getId())); + assert($dataProvider instanceof DataProviderInterface); + + $model = $dataProvider->fetch($dataProvider->getEmptyConfig()->setId($modelId->getId())); } $this->addAjaxPropertyForEditAll(); @@ -421,11 +502,18 @@ public function delete(Action $action) */ public function move(Action $action) { - if ($this->getEnvironment()->getDataDefinition()->getBasicDefinition()->isEditOnlyMode()) { + $environment = $this->getEnvironment(); + assert($environment instanceof EnvironmentInterface); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + + if ($definition->getBasicDefinition()->isEditOnlyMode()) { return $this->edit($action); } - return \vsprintf($this->notImplMsg, 'move - Mode'); + return \vsprintf($this->notImplMsg, ['move - Mode']); } /** @@ -433,11 +521,17 @@ public function move(Action $action) */ public function undo(Action $action) { - if ($this->getEnvironment()->getDataDefinition()->getBasicDefinition()->isEditOnlyMode()) { + $environment = $this->getEnvironment(); + assert($environment instanceof EnvironmentInterface); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + if ($definition->getBasicDefinition()->isEditOnlyMode()) { return $this->edit($action); } - return \vsprintf($this->notImplMsg, 'undo - Mode'); + return \vsprintf($this->notImplMsg, ['undo - Mode']); } /** @@ -463,13 +557,19 @@ public function edit(Action $action) */ public function showAll(Action $action) { - if ($this->getEnvironment()->getDataDefinition()->getBasicDefinition()->isEditOnlyMode()) { + $environment = $this->getEnvironment(); + assert($environment instanceof EnvironmentInterface); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + if ($definition->getBasicDefinition()->isEditOnlyMode()) { return $this->edit($action); } return \sprintf( $this->notImplMsg, - 'showAll - Mode ' . $this->getEnvironment()->getDataDefinition()->getBasicDefinition()->getMode() + 'showAll - Mode ' . (string) ($definition->getBasicDefinition()->getMode() ?? '') ); } @@ -480,7 +580,10 @@ public function showAll(Action $action) */ protected function generateHeaderButtons() { - return (new GlobalButtonRenderer($this->getEnvironment()))->render(); + $environment = $this->getEnvironment(); + assert($environment instanceof EnvironmentInterface); + + return (new GlobalButtonRenderer($environment))->render(); } /** @@ -508,14 +611,18 @@ protected function panel($ignoredPanels = []) public function breadcrumb() { $environment = $this->getEnvironment(); + assert($environment instanceof EnvironmentInterface); + + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); $event = new GetBreadcrumbEvent($environment); - $environment->getEventDispatcher()->dispatch($event, $event::NAME); + $dispatcher->dispatch($event, $event::NAME); $elements = $event->getElements(); - if (!\is_array($elements) || !\count($elements)) { - return null; + if (!\count($elements)) { + return ''; } $GLOBALS['TL_CSS']['cca.dc-general.generalBreadcrumb'] = 'bundles/ccadcgeneral/css/generalBreadcrumb.css'; @@ -532,7 +639,14 @@ public function breadcrumb() */ private function addAjaxPropertyForEditAll() { - $inputProvider = $this->getEnvironment()->getInputProvider(); + $environment = $this->getEnvironment(); + assert($environment instanceof EnvironmentInterface); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); if ( ('select' !== $inputProvider->getParameter('act')) @@ -547,7 +661,7 @@ private function addAjaxPropertyForEditAll() return; } - $propertiesDefinition = $this->getEnvironment()->getDataDefinition()->getPropertiesDefinition(); + $propertiesDefinition = $definition->getPropertiesDefinition(); $propertyClass = \get_class($originalProperty); @@ -568,22 +682,32 @@ private function addAjaxPropertyForEditAll() /** * Find the original property by the modelId. * - * @param string $propertyName The property name. + * @param string|null $propertyName The property name. * * @return PropertyInterface|null */ - private function findOriginalPropertyByModelId($propertyName) + private function findOriginalPropertyByModelId(?string $propertyName): ?PropertyInterface { if (null === $propertyName) { return null; } - $inputProvider = $this->getEnvironment()->getInputProvider(); - $sessionStorage = $this->getEnvironment()->getSessionStorage(); + $environment = $this->getEnvironment(); + assert($environment instanceof EnvironmentInterface); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + + $sessionStorage = $environment->getSessionStorage(); + assert($sessionStorage instanceof SessionStorageInterface); $selectAction = $inputProvider->getParameter('select'); - $session = $sessionStorage->get($this->getEnvironment()->getDataDefinition()->getName() . '.' . $selectAction); + /** @var array{models: list} $session */ + $session = $sessionStorage->get($definition->getName() . '.' . $selectAction); $originalPropertyName = null; foreach ($session['models'] as $modelId) { @@ -599,7 +723,11 @@ private function findOriginalPropertyByModelId($propertyName) $originalPropertyName = \substr($propertyName, \strlen($propertyNamePrefix)); } - $propertiesDefinition = $this->getEnvironment()->getDataDefinition()->getPropertiesDefinition(); + if (!$originalPropertyName) { + return null; + } + + $propertiesDefinition = $definition->getPropertiesDefinition(); if (!$propertiesDefinition->hasProperty($originalPropertyName)) { return null; } diff --git a/src/Contao/View/Contao2BackendView/ButtonRenderer.php b/src/Contao/View/Contao2BackendView/ButtonRenderer.php index de0de205..a3650ff2 100644 --- a/src/Contao/View/Contao2BackendView/ButtonRenderer.php +++ b/src/Contao/View/Contao2BackendView/ButtonRenderer.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2022 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -17,7 +17,7 @@ * @author Ingolf Steinhardt * @author Richard Henkenjohann * @author David Molineus - * @copyright 2013-2022 Contao Community Alliance. + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -31,16 +31,19 @@ use ContaoCommunityAlliance\Contao\Bindings\ContaoEvents; use ContaoCommunityAlliance\Contao\Bindings\Events\Backend\AddToUrlEvent; use ContaoCommunityAlliance\Contao\Bindings\Events\Image\GenerateHtmlEvent; +use ContaoCommunityAlliance\DcGeneral\Clipboard\ClipboardInterface; use ContaoCommunityAlliance\DcGeneral\Clipboard\Filter; use ContaoCommunityAlliance\DcGeneral\Clipboard\ItemInterface; use ContaoCommunityAlliance\DcGeneral\Contao\DataDefinition\Definition\Contao2BackendViewDefinitionInterface; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\GetOperationButtonEvent; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\GetPasteButtonEvent; +use ContaoCommunityAlliance\DcGeneral\Controller\ControllerInterface; use ContaoCommunityAlliance\DcGeneral\Controller\ModelCollector; use ContaoCommunityAlliance\DcGeneral\Data\CollectionInterface; use ContaoCommunityAlliance\DcGeneral\Data\ModelId; use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; use ContaoCommunityAlliance\DcGeneral\Data\MultiLanguageDataProviderInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\CommandCollectionInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\CommandInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\CopyCommandInterface; @@ -48,11 +51,13 @@ use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\ToggleCommandInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\TranslatedToggleCommandInterface; use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; +use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralInvalidArgumentException; +use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; use ContaoCommunityAlliance\Translator\TranslatorInterface; use Symfony\Component\EventDispatcher\EventDispatcherInterface; /** - * This class is an helper for rendering the operation buttons in the views. + * This class is a helper for rendering the operation buttons in the views. * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) @@ -62,14 +67,14 @@ class ButtonRenderer /** * The ids of all circular contained models. * - * @var string[] + * @var list */ private $circularModelIds; /** * The clipboard items in use. * - * @var ItemInterface[] + * @var list */ private $clipboardItems; @@ -115,23 +120,37 @@ class ButtonRenderer */ public function __construct(EnvironmentInterface $environment) { - $this->environment = $environment; - $this->translator = $environment->getTranslator(); - $this->eventDispatcher = $environment->getEventDispatcher(); - $this->clipboardItems = $this->calculateClipboardItems(); - $dataDefinition = $environment->getDataDefinition(); - $this->commands = $dataDefinition - ->getDefinition(Contao2BackendViewDefinitionInterface::NAME) - ->getModelCommands(); - $controller = $environment->getController(); + $this->environment = $environment; + + $translator = $environment->getTranslator(); + assert($translator instanceof TranslatorInterface); + $this->translator = $translator; + + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + $this->eventDispatcher = $dispatcher; + + + $dataDefinition = $environment->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + + $this->clipboardItems = $this->calculateClipboardItems(); + + $backendView = $dataDefinition->getDefinition(Contao2BackendViewDefinitionInterface::NAME); + assert($backendView instanceof Contao2BackendViewDefinitionInterface); + + $this->commands = $backendView->getModelCommands(); + $controller = $environment->getController(); + assert($controller instanceof ControllerInterface); + $this->clipboardModels = $controller->getModelsFromClipboardItems($this->clipboardItems); $this->circularModelIds = []; // We must only check for CUT operation here as pasting copy'ed parents is allowed. - $cutItems = \array_filter($this->clipboardItems, function ($item) { - /** @var ItemInterface $item */ - return $item->getAction() === $item::CUT; - }); + $cutItems = \array_values(\array_filter( + $this->clipboardItems, + static fn (ItemInterface $item): bool => $item->getAction() === $item::CUT + )); $cutModels = $controller->getModelsFromClipboardItems($cutItems); $collector = new ModelCollector($environment); foreach ($cutModels as $model) { @@ -163,9 +182,9 @@ public function renderButtonsForCollection(CollectionInterface $collection) /** * Render the operation buttons for the passed model. * - * @param ModelInterface $model The model to render the buttons for. - * @param ModelInterface $previous The previous model in the collection. - * @param ModelInterface $next The next model in the collection. + * @param ModelInterface $model The model to render the buttons for. + * @param ModelInterface|null $previous The previous model in the collection. + * @param ModelInterface|null $next The next model in the collection. * * @return void */ @@ -203,14 +222,18 @@ private function renderButtonsFor( $buttonEvent ->setModel($model) ->setCircularReference($isCircular) - ->setPrevious($previous) - ->setNext($next) ->setHrefAfter($urlAfter) ->setHrefInto($urlInto) // Check if the id is in the ignore list. ->setPasteAfterDisabled($isCircular) ->setPasteIntoDisabled($isCircular) ->setContainedModels($this->clipboardModels); + if (null !== $previous) { + $buttonEvent->setPrevious($previous); + } + if (null !== $next) { + $buttonEvent->setNext($next); + } $this->eventDispatcher->dispatch($buttonEvent, GetPasteButtonEvent::NAME); $buttons['pasteafter'] = $this->renderPasteAfterButton($buttonEvent); @@ -233,6 +256,8 @@ private function renderButtonsFor( private function isHierarchical() { $dataDefinition = $this->environment->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + $basicDefinition = $dataDefinition->getBasicDefinition(); return $basicDefinition::MODE_HIERARCHICAL === $basicDefinition->getMode(); @@ -256,8 +281,11 @@ private function hasPasteButtons() */ private function hasPasteNewButton() { - $environment = $this->environment; - $basicDefinition = $environment->getDataDefinition()->getBasicDefinition(); + $environment = $this->environment; + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $basicDefinition = $definition->getBasicDefinition(); return ((true === (bool) ViewHelpers::getManualSortingProperty($environment)) && (true === empty($this->clipboardItems)) @@ -268,13 +296,13 @@ private function hasPasteNewButton() /** * Render a command button. * - * @param CommandInterface $command The command to render the button for. - * @param ModelInterface $model The model to which the command shall get applied. - * @param ModelInterface $previous The previous model in the collection. - * @param ModelInterface $next The next model in the collection. - * @param bool $isCircularReference Determinator if there exists a circular reference between the model - * and the model(s) contained in the clipboard. - * @param string[] $childIds The ids of all child models. + * @param CommandInterface $command The command to render the button for. + * @param ModelInterface $model The model to which the command shall get applied. + * @param ModelInterface|null $previous The previous model in the collection. + * @param ModelInterface|null $next The next model in the collection. + * @param bool $isCircularReference Determinator if there exists a circular reference between the + * model and the model(s) contained in the clipboard. + * @param string[] $childIds The ids of all child models. * * @return string */ @@ -309,7 +337,7 @@ private function buildCommand($command, $model, $previous, $next, $isCircularRef ->setObjModel($model) ->setAttributes($attributes) ->setLabel($this->getCommandLabel($command)) - ->setTitle(\sprintf($this->translate((string) $command->getDescription()), $model->getID())) + ->setTitle(\sprintf($this->translate($command->getDescription()), $model->getID())) ->setHref($this->calculateHref($command, $model)) ->setChildRecordIds($childIds) ->setCircularReference($isCircularReference) @@ -331,12 +359,12 @@ private function buildCommand($command, $model, $previous, $next, $isCircularRef if ($icon !== Image::getPath($icon)) { $iconDisabledSuffix = '_'; } - $icon = \substr_replace($icon, $iconDisabledSuffix, \strrpos($icon, '.'), 0); + $icon = \substr_replace($icon, $iconDisabledSuffix, \strrpos($icon, '.') ?: \strlen($icon), 0); } return $this->renderImageAsHtml( $icon, - ($buttonEvent->getLabel() ?? ''), + $buttonEvent->getLabel(), \sprintf( 'title="%s" class="%s"', StringUtil::specialchars($this->translator->translate( @@ -352,10 +380,10 @@ private function buildCommand($command, $model, $previous, $next, $isCircularRef return \sprintf( ' %s', $command->getName(), - $buttonEvent->getHref(), + $buttonEvent->getHref() ?? '', StringUtil::specialchars($buttonEvent->getTitle()), \ltrim($buttonEvent->getAttributes()), - $this->renderImageAsHtml($icon, ($buttonEvent->getLabel() ?? '')) + $this->renderImageAsHtml($icon, $buttonEvent->getLabel()) ); } @@ -408,6 +436,8 @@ private function calculateParameters(CommandInterface $command, $serializedModel $parameters['act'] = $command->getName(); $inputProvider = $this->environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + // If we have a pid add it, used for mode 4 and all parent -> current views. if ($inputProvider->hasParameter('pid')) { $parameters['pid'] = $inputProvider->getParameter('pid'); @@ -462,15 +492,18 @@ private function renderPasteIntoButton(GetPasteButtonEvent $event) return $this->renderImageAsHtml('pasteinto_.svg', $label, 'class="blink"'); } + $model = $event->getModel(); + assert($model instanceof ModelInterface); + if ('pasteinto.1' !== ($opDesc = $this->translate('pasteinto.1'))) { - $title = \sprintf($opDesc, $event->getModel()->getId()); + $title = \sprintf($opDesc, $model->getId()); } else { - $title = \sprintf('%s id %s', $label, $event->getModel()->getId()); + $title = \sprintf('%s id %s', $label, $model->getId()); } return \sprintf( ' %s', - $event->getHrefInto(), + $event->getHrefInto() ?? '', StringUtil::specialchars($title), $this->renderImageAsHtml('pasteinto.svg', $label, 'class="blink"') ); @@ -494,15 +527,18 @@ private function renderPasteAfterButton(GetPasteButtonEvent $event) return $this->renderImageAsHtml('pasteafter_.svg', $label, 'class="blink"'); } + $model = $event->getModel(); + assert($model instanceof ModelInterface); + if ('pasteafter.1' !== ($opDesc = $this->translate('pasteafter.1'))) { - $title = \sprintf($opDesc, $event->getModel()->getId()); + $title = \sprintf($opDesc, $model->getId()); } else { - $title = \sprintf('%s id %s', $label, $event->getModel()->getId()); + $title = \sprintf('%s id %s', $label, $model->getId()); } return \sprintf( ' %s', - $event->getHrefAfter(), + $event->getHrefAfter() ?? '', StringUtil::specialchars($title), $this->renderImageAsHtml('pasteafter.svg', $label, 'class="blink"') ); @@ -511,16 +547,23 @@ private function renderPasteAfterButton(GetPasteButtonEvent $event) /** * Calculate all clipboard items for the current view. * - * @return ItemInterface[] + * @return list */ private function calculateClipboardItems() { $dataDefinition = $this->environment->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + $basicDefinition = $dataDefinition->getBasicDefinition(); $clipboard = $this->environment->getClipboard(); + assert($clipboard instanceof ClipboardInterface); $filter = new Filter(); - $filter->andModelIsFromProvider($basicDefinition->getDataProvider()); + + $dataProvider = $basicDefinition->getDataProvider(); + assert(\is_string($dataProvider)); + + $filter->andModelIsFromProvider($dataProvider); if ($parentProviderName = $basicDefinition->getParentDataProvider()) { $filter->andParentIsFromProvider($parentProviderName); } else { @@ -539,7 +582,10 @@ private function calculateClipboardItems() */ protected function translate($path) { - $value = $this->translator->translate($path, $this->environment->getDataDefinition()->getName()); + $definition = $this->environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $value = $this->translator->translate($path, $definition->getName()); if ($path !== $value) { return $value; } @@ -564,7 +610,7 @@ private function renderImageAsHtml($src, $alt, $attributes = '') ContaoEvents::IMAGE_GET_HTML ); - return $imageEvent->getHtml(); + return $imageEvent->getHtml() ?? ''; } /** @@ -592,8 +638,9 @@ private function addToUrl($parameters) */ private function isTogglerInActiveState($command, $model) { - $dataProvider = $this->environment->getDataProvider($model->getProviderName()); - $propModel = $model; + $dataProvider = $this->environment->getDataProvider($model->getProviderName()); + $propModel = $model; + $toggleProperty = $command->getToggleProperty(); if ( $command instanceof TranslatedToggleCommandInterface @@ -605,16 +652,19 @@ private function isTogglerInActiveState($command, $model) $dataProvider ->getEmptyConfig() ->setId($model->getId()) - ->setFields([$command->getToggleProperty()]) + ->setFields([$toggleProperty]) ); + if (null === $propModel) { + throw new DcGeneralInvalidArgumentException('Model not found: ' . $model->getId()); + } $dataProvider->setCurrentLanguage($language); } if ($command->isInverse()) { - return !$propModel->getProperty($command->getToggleProperty()); + return !$propModel->getProperty($toggleProperty); } - return (bool) $propModel->getProperty($command->getToggleProperty()); + return (bool) $propModel->getProperty($toggleProperty); } /** @@ -645,15 +695,10 @@ private function calculateHref(CommandInterface $command, $model) */ private function getCommandLabel(CommandInterface $command) { - if (null === $label = $command->getLabel()) { + if ('' === $label = $command->getLabel()) { $label = $command->getName(); } - if (\is_array($label)) { - return $this->translate($label[0]); - } - $label = $this->translate($label); - - return \is_array($label) ? $label[0] : $label; + return $this->translate($label); } } diff --git a/src/Contao/View/Contao2BackendView/ContaoBackendViewTemplate.php b/src/Contao/View/Contao2BackendView/ContaoBackendViewTemplate.php index 6f74ad22..59fe663d 100644 --- a/src/Contao/View/Contao2BackendView/ContaoBackendViewTemplate.php +++ b/src/Contao/View/Contao2BackendView/ContaoBackendViewTemplate.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -16,7 +16,8 @@ * @author David Molineus * @author Sven Baumann * @author Richard Henkenjohann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -27,21 +28,25 @@ use Contao\System; use ContaoCommunityAlliance\DcGeneral\Clipboard\Clipboard; use ContaoCommunityAlliance\DcGeneral\Contao\InputProvider; +use ContaoCommunityAlliance\DcGeneral\DataDefinitionContainerInterface; use ContaoCommunityAlliance\DcGeneral\DefaultEnvironment; use ContaoCommunityAlliance\DcGeneral\View\ViewTemplateInterface; use ContaoCommunityAlliance\Translator\TranslatorInterface; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; /** * This class is used for the contao backend view as template. + * + * @psalm-suppress PropertyNotSetInConstructor */ class ContaoBackendViewTemplate extends BackendTemplate implements ViewTemplateInterface, TranslatorInterface { /** * The translator. * - * @var TranslatorInterface + * @var TranslatorInterface|null */ - protected $translator; + protected $translator = null; /** * Get the translator. @@ -50,6 +55,9 @@ class ContaoBackendViewTemplate extends BackendTemplate implements ViewTemplateI */ public function getTranslator() { + if (null === $this->translator) { + throw new \RuntimeException('Translator not set.'); + } return $this->translator; } @@ -67,16 +75,6 @@ public function setTranslator(TranslatorInterface $translator) return $this; } - /** - * {@inheritDoc} - */ - public function setData($data) - { - parent::setData($data); - - return $this; - } - /** * {@inheritDoc} */ @@ -100,11 +98,7 @@ public function get($name) */ public function translate($string, $domain = null, array $parameters = [], $locale = null) { - if ($this->translator) { - return $this->translator->translate($string, $domain, $parameters, $locale); - } - - return $string; + return $this->getTranslator()->translate($string, $domain, $parameters, $locale); } /** @@ -112,11 +106,7 @@ public function translate($string, $domain = null, array $parameters = [], $loca */ public function translatePluralized($string, $number, $domain = null, array $parameters = [], $locale = null) { - if ($this->translator) { - return $this->translator->translatePluralized($string, $number, $domain, $parameters, $locale); - } - - return $string; + return $this->getTranslator()->translatePluralized($string, $number, $domain, $parameters, $locale); } /** @@ -124,17 +114,28 @@ public function translatePluralized($string, $number, $domain = null, array $par * * @return string * - * @SuppressWarnings(PHPMD.Superglobals) + * @deprecated Do not use */ public function getBackButton(): string { - $dataContainer = System::getContainer()->get('cca.dc-general.data-definition-container'); - $dataDefinition = $dataContainer->getDefinition($this->table); + $dataContainer = System::getContainer()->get('cca.dc-general.data-definition-container'); + assert($dataContainer instanceof DataDefinitionContainerInterface); + + /** @psalm-suppress UndefinedThisPropertyFetch */ + $table = $this->table; + assert(\is_string($table)); + $dataDefinition = $dataContainer->getDefinition($table); + + $translator = System::getContainer()->get('cca.translator.contao_translator'); + assert($translator instanceof TranslatorInterface); + + $dispatcher = System::getContainer()->get('event_dispatcher'); + assert($dispatcher instanceof EventDispatcherInterface); $environment = new DefaultEnvironment(); $environment->setDataDefinition($dataDefinition); - $environment->setTranslator(System::getContainer()->get('cca.translator.contao_translator')); - $environment->setEventDispatcher(System::getContainer()->get('event_dispatcher')); + $environment->setTranslator($translator); + $environment->setEventDispatcher($dispatcher); $environment->setInputProvider(new InputProvider()); $environment->setClipboard(new Clipboard()); @@ -150,6 +151,16 @@ public function getData() return parent::getData(); } + /** + * {@inheritDoc} + */ + public function setData($arrData) + { + parent::setData($arrData); + + return $this; + } + /** * {@inheritDoc} */ @@ -163,6 +174,7 @@ public function parse() */ public function output() { + /** @psalm-suppress DeprecatedMethod */ parent::output(); } // @codingStandardsIgnoreEnd diff --git a/src/Contao/View/Contao2BackendView/ContaoWidgetManager.php b/src/Contao/View/Contao2BackendView/ContaoWidgetManager.php index 3f9c39ce..12d3669c 100644 --- a/src/Contao/View/Contao2BackendView/ContaoWidgetManager.php +++ b/src/Contao/View/Contao2BackendView/ContaoWidgetManager.php @@ -37,13 +37,19 @@ use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\DecodePropertyValueForWidgetEvent; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\EncodePropertyValueFromWidgetEvent; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\ResolveWidgetErrorMessageEvent; +use ContaoCommunityAlliance\DcGeneral\Controller\ControllerInterface; use ContaoCommunityAlliance\DcGeneral\Data\ModelId; use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; use ContaoCommunityAlliance\DcGeneral\Data\PropertyValueBag; use ContaoCommunityAlliance\DcGeneral\Data\PropertyValueBagInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralInvalidArgumentException; use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralRuntimeException; +use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; +use ContaoCommunityAlliance\DcGeneral\SessionStorageInterface; +use ContaoCommunityAlliance\Translator\TranslatorInterface; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; /** * Class ContaoWidgetManager. @@ -60,6 +66,8 @@ class ContaoWidgetManager * The environment in use. * * @var ContaoFrameworkInterface + * + * @psalm-suppress DeprecatedInterface */ protected $framework; @@ -87,7 +95,10 @@ public function __construct(EnvironmentInterface $environment, ModelInterface $m { $this->environment = $environment; $this->model = $model; - $this->framework = System::getContainer()->get('contao.framework'); + $framework = System::getContainer()->get('contao.framework'); + assert($framework instanceof ContaoFrameworkInterface); + + $this->framework = $framework; } /** @@ -102,13 +113,15 @@ public function __construct(EnvironmentInterface $environment, ModelInterface $m public function encodeValue($property, $value, PropertyValueBag $propertyValues) { $environment = $this->getEnvironment(); + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); $event = new EncodePropertyValueFromWidgetEvent($environment, $this->model, $propertyValues); $event ->setProperty($property) ->setValue($value); - $environment->getEventDispatcher()->dispatch($event, EncodePropertyValueFromWidgetEvent::NAME); + $dispatcher->dispatch($event, EncodePropertyValueFromWidgetEvent::NAME); return $event->getValue(); } @@ -124,18 +137,20 @@ public function encodeValue($property, $value, PropertyValueBag $propertyValues) public function decodeValue($property, $value) { $environment = $this->getEnvironment(); + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); $event = new DecodePropertyValueForWidgetEvent($environment, $this->model); $event ->setProperty($property) ->setValue($value); - $environment->getEventDispatcher()->dispatch($event, DecodePropertyValueForWidgetEvent::NAME); + $dispatcher->dispatch($event, DecodePropertyValueForWidgetEvent::NAME); return $event->getValue(); } /** - * {@inheritdoc} + * @return EnvironmentInterface */ public function getEnvironment() { @@ -143,12 +158,14 @@ public function getEnvironment() } /** - * {@inheritDoc} + * @param string $property + * + * @return bool */ public function hasWidget($property) { try { - return null !== $this->getWidget($property); + return ($this->getWidget($property) instanceof Widget); // @codingStandardsIgnoreStart } catch (\Exception $e) { // Fall though and return false. @@ -167,10 +184,12 @@ public function hasWidget($property) */ public function loadRichTextEditor($buffer, Widget $widget) { + /** @psalm-suppress UndefinedMagicPropertyFetch */ + $rte = $widget->rte; if ( - (null === $widget->rte) - || ((0 !== (\strncmp($widget->rte, 'tiny', 4))) - && (0 !== \strncmp($widget->rte, 'ace', 3))) + (null === $rte) + || ((0 !== (\strncmp($rte, 'tiny', 4))) + && (0 !== \strncmp($rte, 'ace', 3))) ) { return $buffer; } @@ -178,7 +197,7 @@ public function loadRichTextEditor($buffer, Widget $widget) $backendAdapter = $this->framework->getAdapter(Backend::class); $templateLoader = $this->framework->getAdapter(TemplateLoader::class); - [$file, $type] = \explode('|', $widget->rte) + [null, null]; + [$file, $type] = \explode('|', $rte) + ['', '']; $templateName = 'be_' . $file; // This test if the rich text editor template exist. @@ -188,9 +207,9 @@ public function loadRichTextEditor($buffer, Widget $widget) $template ->set('selector', 'ctrl_' . $widget->id) ->set('type', $type) - ->set('readonly', (bool) $widget->readonly); + ->set('readonly', $widget->readonly); - if (0 !== \strncmp($widget->rte, 'tiny', 4)) { + if (0 !== \strncmp($rte, 'tiny', 4)) { /** @deprecated Deprecated since Contao 4.0, to be removed in Contao 5.0 */ $template->set('language', $backendAdapter->getTinyMceLanguage()); } @@ -210,7 +229,10 @@ public function loadRichTextEditor($buffer, Widget $widget) protected function getUniqueId($propertyName) { $inputProvider = $this->getEnvironment()->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + $sessionStorage = $this->getEnvironment()->getSessionStorage(); + assert($sessionStorage instanceof SessionStorageInterface); $selector = 'ctrl_' . $propertyName; @@ -237,21 +259,25 @@ protected function getUniqueId($propertyName) /** * Retrieve the instance of a widget for the given property. * - * @param string $property Name of the property for which the widget shall be retrieved. - * @param PropertyValueBag $inputValues The input values to use (optional). + * @param string $property Name of the property for which the widget shall be retrieved. + * @param PropertyValueBagInterface $inputValues The input values to use (optional). * - * @return Widget + * @return Widget|null * - * @throws DcGeneralRuntimeException When No widget could be build. + * @throws DcGeneralRuntimeException When No widget could be built. * @throws DcGeneralInvalidArgumentException When property is not defined in the property definitions. * * @SuppressWarnings(PHPMD.Superglobals) * @SuppressWarnings(PHPMD.CamelCaseVariableName) */ - public function getWidget($property, PropertyValueBag $inputValues = null) + public function getWidget($property, PropertyValueBagInterface $inputValues = null) { - $environment = $this->getEnvironment(); - $propertyDefinitions = $environment->getDataDefinition()->getPropertiesDefinition(); + $environment = $this->getEnvironment(); + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + + $propertyDefinitions = $definition->getPropertiesDefinition(); if (!$propertyDefinitions->hasProperty($property)) { throw new DcGeneralInvalidArgumentException( @@ -263,18 +289,19 @@ public function getWidget($property, PropertyValueBag $inputValues = null) $model->setId($this->model->getId()); if ($inputValues) { + $controller = $environment->getController(); + assert($controller instanceof ControllerInterface); + $values = new PropertyValueBag($inputValues->getArrayCopy()); - $this->environment->getController()->updateModelFromPropertyBag($model, $values); + $controller->updateModelFromPropertyBag($model, $values); } $event = new BuildWidgetEvent($environment, $model, $propertyDefinitions->getProperty($property)); - $environment->getEventDispatcher()->dispatch($event, $event::NAME); - if (!$event->getWidget()) { - throw new DcGeneralRuntimeException( - \sprintf('Widget was not build for property %s::%s.', $this->model->getProviderName(), $property) - ); - } + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $dispatcher->dispatch($event, $event::NAME); return $event->getWidget(); } @@ -292,7 +319,9 @@ public function getWidget($property, PropertyValueBag $inputValues = null) protected function buildDatePicker($objWidget) { $translator = $this->getEnvironment()->getTranslator(); - $strFormat = $GLOBALS['TL_CONFIG'][$objWidget->rgxp . 'Format']; + assert($translator instanceof TranslatorInterface); + + $strFormat = $GLOBALS['TL_CONFIG'][$objWidget->rgxp . 'Format']; switch ($objWidget->rgxp) { case 'datim': @@ -330,14 +359,15 @@ protected function buildDatePicker($objWidget) */ protected function generateHelpText($property) { - $propInfo = $this->getEnvironment()->getDataDefinition()->getPropertiesDefinition()->getProperty($property); + $definition = $this->getEnvironment()->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $propInfo = $definition->getPropertiesDefinition()->getProperty($property); $label = $propInfo->getDescription(); $widgetType = $propInfo->getWidgetType(); if ( - empty($label) - || ('password' === $widgetType) - || !\is_string($label) + ('password' === $widgetType) || !$GLOBALS['TL_CONFIG']['showHelp'] ) { return ''; @@ -349,31 +379,34 @@ protected function generateHelpText($property) /** * Render the widget for the named property. * - * @param string $property The name of the property for which the widget shall be rendered. - * @param bool $ignoreErrors Flag if the error property of the widget shall get cleared prior rendering. - * @param PropertyValueBag $inputValues The input values to use (optional). + * @param string $property The name of the property for which the widget shall be rendered. + * @param bool $ignoreErrors Flag if the error property of the widget shall get + * cleared prior rendering. + * @param PropertyValueBagInterface $inputValues The input values to use (optional). * * @return string * * @throws DcGeneralRuntimeException For unknown properties. */ - public function renderWidget($property, $ignoreErrors = false, PropertyValueBag $inputValues = null) + public function renderWidget($property, $ignoreErrors = false, PropertyValueBagInterface $inputValues = null) { /** @var Widget $widget */ $widget = $this->getWidget($property, $inputValues); - if (!$widget) { - throw new DcGeneralRuntimeException('No widget for property ' . $property); - } $this->cleanErrors($widget, $ignoreErrors); $this->widgetAddError($property, $widget, $inputValues, $ignoreErrors); - $propInfo = $this->getEnvironment()->getDataDefinition()->getPropertiesDefinition()->getProperty($property); + $definition = $this->getEnvironment()->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $propInfo = $definition->getPropertiesDefinition()->getProperty($property); + /** @psalm-suppress UndefinedMagicPropertyFetch */ $isHideInput = (bool) $widget->hideInput; $hiddenFields = ($isHideInput) ? $this->buildHiddenFields($widget->value, $widget->name) : null; + /** @psalm-suppress UndefinedMagicPropertyFetch */ $content = (new ContaoBackendViewTemplate('dcbe_general_field')) ->set('strName', $property) ->set('strClass', $widget->tl_class) @@ -406,7 +439,7 @@ public function renderWidget($property, $ignoreErrors = false, PropertyValueBag */ public function buildHiddenFields($value, string $propertyName): array { - if (is_string($value)) { + if (\is_string($value)) { return [$propertyName => $value]; } @@ -415,7 +448,7 @@ public function buildHiddenFields($value, string $propertyName): array $values[] = $this->buildHiddenFields($item, $propertyName . '[' . $key . ']'); } - return array_merge(...$values); + return \array_merge(...$values); } /** @@ -424,7 +457,7 @@ public function buildHiddenFields($value, string $propertyName): array * @SuppressWarnings(PHPMD.Superglobals) * @SuppressWarnings(PHPMD.CamelCaseVariableName) */ - public function processInput(PropertyValueBag $propertyValues) + public function processInput(PropertyValueBag $propertyValues): void { // @codingStandardsIgnoreStart - Remember current POST data and clear it. $post = $_POST; @@ -441,8 +474,10 @@ public function processInput(PropertyValueBag $propertyValues) foreach (\array_keys($propertyValues->getArrayCopy()) as $property) { // NOTE: the passed input values are RAW DATA from the input provider - aka widget known values and not // native data as in the model. - // Therefore we do not need to decode them but MUST encode them. + // Therefore, we do not need to decode them but MUST encode them. $widget = $this->getWidget($property, $propertyValues); + assert($widget instanceof Widget); + $widget->validate(); if ($widget->hasErrors()) { @@ -471,7 +506,7 @@ public function processInput(PropertyValueBag $propertyValues) /** * {@inheritDoc} */ - public function processErrors(PropertyValueBag $propertyValues) + public function processErrors(PropertyValueBag $propertyValues): void { $propertyErrors = $propertyValues->getInvalidPropertyErrors(); @@ -480,9 +515,11 @@ public function processErrors(PropertyValueBag $propertyValues) } $dispatcher = $this->getEnvironment()->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); foreach ($propertyErrors as $property => $errors) { $widget = $this->getWidget($property); + assert($widget instanceof Widget); foreach ($errors as $error) { $event = new ResolveWidgetErrorMessageEvent($this->getEnvironment(), $error); @@ -499,6 +536,8 @@ public function processErrors(PropertyValueBag $propertyValues) * @param bool $ignoreErrors The flag for errors cleared. * * @return void + * + * @throws \ReflectionException */ protected function cleanErrors(Widget $widget, $ignoreErrors = false) { diff --git a/src/Contao/View/Contao2BackendView/Controller/ClipboardController.php b/src/Contao/View/Contao2BackendView/Controller/ClipboardController.php index f93d1ddd..b393ee96 100644 --- a/src/Contao/View/Contao2BackendView/Controller/ClipboardController.php +++ b/src/Contao/View/Contao2BackendView/Controller/ClipboardController.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2021 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -16,7 +16,8 @@ * @author David Molineus * @author Stefan Heimes * @author Sven Baumann - * @copyright 2013-2021 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -26,6 +27,7 @@ use ContaoCommunityAlliance\Contao\Bindings\ContaoEvents; use ContaoCommunityAlliance\Contao\Bindings\Events\Backend\AddToUrlEvent; use ContaoCommunityAlliance\Contao\Bindings\Events\Controller\RedirectEvent; +use ContaoCommunityAlliance\DcGeneral\Clipboard\ClipboardInterface; use ContaoCommunityAlliance\DcGeneral\Clipboard\Filter; use ContaoCommunityAlliance\DcGeneral\Clipboard\Item; use ContaoCommunityAlliance\DcGeneral\Clipboard\ItemInterface; @@ -33,13 +35,18 @@ use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\ContaoBackendViewTemplate; use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminator; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\ViewHelpers; +use ContaoCommunityAlliance\DcGeneral\Data\DataProviderInterface; use ContaoCommunityAlliance\DcGeneral\Data\ModelId; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\DcGeneralEvents; use ContaoCommunityAlliance\DcGeneral\DcGeneralViews; use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; use ContaoCommunityAlliance\DcGeneral\Event\ActionEvent; use ContaoCommunityAlliance\DcGeneral\Event\FormatModelLabelEvent; use ContaoCommunityAlliance\DcGeneral\Event\ViewEvent; +use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; +use ContaoCommunityAlliance\Translator\TranslatorInterface; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\EventDispatcher\EventSubscriberInterface; /** @@ -123,8 +130,15 @@ private function checkPermission(ActionEvent $event) { $actionName = $event->getAction()->getName(); - $environment = $event->getEnvironment(); - $basicDefinition = $environment->getDataDefinition()->getBasicDefinition(); + $environment = $event->getEnvironment(); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + + $basicDefinition = $definition->getBasicDefinition(); if ( (('create' === $actionName) && (true === $basicDefinition->isCreatable())) @@ -137,11 +151,11 @@ private function checkPermission(ActionEvent $event) $permissionMessage = 'You have no permission for model ' . $actionName . ' '; switch ($actionName) { case 'create': - $permissionMessage .= 'in ' . $environment->getDataDefinition()->getName(); + $permissionMessage .= 'in ' . $definition->getName(); break; case 'cut': - $permissionMessage .= $environment->getInputProvider()->getParameter('source'); + $permissionMessage .= $inputProvider->getParameter('source'); break; default: @@ -167,10 +181,16 @@ private function checkPermission(ActionEvent $event) */ private function clearClipboard(ActionEvent $event, $redirect = true) { - $environment = $event->getEnvironment(); + $environment = $event->getEnvironment(); + $eventDispatcher = $environment->getEventDispatcher(); - $clipboard = $environment->getClipboard(); - $input = $environment->getInputProvider(); + assert($eventDispatcher instanceof EventDispatcherInterface); + + $clipboard = $environment->getClipboard(); + assert($clipboard instanceof ClipboardInterface); + + $input = $environment->getInputProvider(); + assert($input instanceof InputProviderInterface); if ($clipboardId = $input->getParameter('clipboard-item')) { $clipboard->removeByClipboardId($clipboardId); @@ -224,8 +244,13 @@ private function addToClipboard(ActionEvent $event) { $actionName = $event->getAction()->getName(); $environment = $event->getEnvironment(); - $input = $environment->getInputProvider(); - $clipboard = $environment->getClipboard(); + + $input = $environment->getInputProvider(); + assert($input instanceof InputProviderInterface); + + $clipboard = $environment->getClipboard(); + assert($clipboard instanceof ClipboardInterface); + $parentIdRaw = $input->getParameter('pid'); if ($parentIdRaw) { @@ -243,7 +268,12 @@ private function addToClipboard(ActionEvent $event) return; } - $providerName = $environment->getDataDefinition()->getBasicDefinition()->getDataProvider(); + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $providerName = $definition->getBasicDefinition()->getDataProvider(); + assert(\is_string($providerName)); + $item = new UnsavedItem($clipboardActionName, $parentId, $providerName); // Remove other create items, there can only be one create item in the clipboard or many others @@ -287,6 +317,7 @@ private function addToClipboard(ActionEvent $event) protected function isAddingAllowed(EnvironmentInterface $environment) { $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); // No manual sorting property defined, no need to add it to the clipboard. // Or we already have an after or into attribute, a handler can pick it up. @@ -313,23 +344,38 @@ public function handleView(ViewEvent $event) return; } - $environment = $event->getEnvironment(); - $input = $environment->getInputProvider(); + $environment = $event->getEnvironment(); + + $input = $environment->getInputProvider(); + assert($input instanceof InputProviderInterface); + $eventDispatcher = $environment->getEventDispatcher(); - $basicDefinition = $environment->getDataDefinition()->getBasicDefinition(); + assert($eventDispatcher instanceof EventDispatcherInterface); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $basicDefinition = $definition->getBasicDefinition(); + + $dataProvider = $basicDefinition->getDataProvider(); + assert(\is_string($dataProvider)); $filter = new Filter(); - $filter->andModelIsFromProvider($basicDefinition->getDataProvider()); + $filter->andModelIsFromProvider($dataProvider); if ($parentProviderName = $basicDefinition->getParentDataProvider()) { $filter->andParentIsFromProvider($parentProviderName); } else { $filter->andHasNoParent(); } + $clipboard = $environment->getClipboard(); + assert($clipboard instanceof ClipboardInterface); + $options = []; - foreach ($environment->getClipboard()->fetch($filter) as $item) { + foreach ($clipboard->fetch($filter) as $item) { $modelId = $item->getModelId(); $dataProvider = $environment->getDataProvider($item->getDataProviderName()); + assert($dataProvider instanceof DataProviderInterface); if ($modelId) { $config = $dataProvider->getEmptyConfig(); @@ -348,7 +394,11 @@ public function handleView(ViewEvent $event) $label = $label['content']; } else { $model = $dataProvider->getEmptyModel(); - $label = $environment->getTranslator()->translate('new.0', $item->getDataProviderName()); + + $translator = $environment->getTranslator(); + assert($translator instanceof TranslatorInterface); + + $label = $translator->translate('new.0', $item->getDataProviderName()); } $options[$item->getClipboardId()] = ['item' => $item, 'model' => $model, 'label' => $label]; @@ -385,13 +435,18 @@ public function handleView(ViewEvent $event) */ private function removeItemsFromClipboard(ActionEvent $event) { - $environment = $event->getEnvironment(); - if ('select' === $environment->getInputProvider()->getParameter('act')) { + $environment = $event->getEnvironment(); + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + + if ('select' === $inputProvider->getParameter('act')) { return; } $clipboard = $environment->getClipboard(); - $filter = new Filter(); + assert($clipboard instanceof ClipboardInterface); + + $filter = new Filter(); $filter->andActionIs(ItemInterface::CREATE); $items = $clipboard->fetch($filter); foreach ($items as $item) { diff --git a/src/Contao/View/Contao2BackendView/EditMask.php b/src/Contao/View/Contao2BackendView/EditMask.php index 2a4704f8..64cadd2e 100644 --- a/src/Contao/View/Contao2BackendView/EditMask.php +++ b/src/Contao/View/Contao2BackendView/EditMask.php @@ -26,6 +26,7 @@ namespace ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView; use Contao\BackendUser; +use Contao\CoreBundle\Intl\Locales; use Contao\Image; use Contao\System; use ContaoCommunityAlliance\Contao\Bindings\ContaoEvents; @@ -33,13 +34,18 @@ use ContaoCommunityAlliance\Contao\Bindings\Events\Controller\RedirectEvent; use ContaoCommunityAlliance\Contao\Bindings\Events\System\GetReferrerEvent; use ContaoCommunityAlliance\Contao\Bindings\Events\System\LogEvent; +use ContaoCommunityAlliance\DcGeneral\Clipboard\ClipboardInterface; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\GetEditMaskSubHeadlineEvent; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\GetEditModeButtonsEvent; +use ContaoCommunityAlliance\DcGeneral\Controller\ControllerInterface; +use ContaoCommunityAlliance\DcGeneral\Data\DataProviderInterface; use ContaoCommunityAlliance\DcGeneral\Data\DefaultEditInformation; +use ContaoCommunityAlliance\DcGeneral\Data\EditInformationInterface; use ContaoCommunityAlliance\DcGeneral\Data\ModelId; use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; use ContaoCommunityAlliance\DcGeneral\Data\MultiLanguageDataProviderInterface; use ContaoCommunityAlliance\DcGeneral\Data\PropertyValueBag; +use ContaoCommunityAlliance\DcGeneral\Data\PropertyValueBagInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\BasicDefinitionInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\PropertiesDefinitionInterface; @@ -53,6 +59,10 @@ use ContaoCommunityAlliance\DcGeneral\Event\PrePersistModelEvent; use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralInvalidArgumentException; use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralRuntimeException; +use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; +use ContaoCommunityAlliance\DcGeneral\SessionStorageInterface; +use ContaoCommunityAlliance\Translator\TranslatorInterface; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; /** * This class manages the displaying of the edit/create mask containing the widgets. @@ -61,6 +71,7 @@ * * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * @SuppressWarnings(PHPMD.ExcessiveClassLength) */ class EditMask { @@ -109,20 +120,20 @@ class EditMask /** * The default edit information. * - * @var DefaultEditInformation + * @var EditInformationInterface */ - private DefaultEditInformation $editInformation; + private EditInformationInterface $editInformation; /** * Create the edit mask. * - * @param BackendViewInterface $view The view in use. - * @param ModelInterface $model The model with the current data. - * @param ModelInterface $originalModel The data from the original data. - * @param callable $preFunction The function to call before saving an item. - * @param callable $postFunction The function to call after saving an item. - * @param string $breadcrumb The rendered breadcrumb. - * @param DefaultEditInformation $editInformation The default edit information. + * @param BackendViewInterface $view The view in use. + * @param ModelInterface $model The model with the current data. + * @param ModelInterface $originalModel The data from the original data. + * @param callable|null $preFunction The function to call before saving an item. + * @param callable|null $postFunction The function to call after saving an item. + * @param string $breadcrumb The rendered breadcrumb. + * @param EditInformationInterface|null $editInformation The default edit information. */ public function __construct( $view, @@ -131,9 +142,12 @@ public function __construct( $preFunction, $postFunction, $breadcrumb, - ?DefaultEditInformation $editInformation = null + ?EditInformationInterface $editInformation = null ) { - $this->environment = $view->getEnvironment(); + if (null === $environment = $view->getEnvironment()) { + throw new \InvalidArgumentException('View has no environment'); + } + $this->environment = $environment; $this->model = $model; $this->originalModel = $originalModel; $this->preFunction = $preFunction; @@ -148,6 +162,7 @@ public function __construct( ); // @codingStandardsIgnoreEnd $editInformation = System::getContainer()->get('cca.dc-general.edit-information'); + assert($editInformation instanceof EditInformationInterface); } $this->editInformation = $editInformation; @@ -170,7 +185,10 @@ public function getEnvironment() */ protected function getDataDefinition() { - return $this->getEnvironment()->getDataDefinition(); + $definition = $this->getEnvironment()->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + return $definition; } /** @@ -180,7 +198,10 @@ protected function getDataDefinition() */ protected function isPopup() { - return $this->getEnvironment()->getInputProvider()->getParameter('popup'); + $inputProvider = $this->getEnvironment()->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + + return $inputProvider->getParameter('popup'); } /** @@ -200,7 +221,11 @@ protected function checkEditable($model) // Check if table is editable. if ($model->getId() && !$definition->getBasicDefinition()->isEditable()) { $message = 'DataContainer ' . $definition->getName() . ' is not editable'; - $environment->getEventDispatcher()->dispatch( + + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $dispatcher->dispatch( new LogEvent($message, 'ERROR', 'DC_General - edit()'), ContaoEvents::SYSTEM_LOG ); @@ -225,7 +250,11 @@ protected function checkCreatable($model) // Check if table is closed, but we are adding a new item. if (!($model->getId() || $definition->getBasicDefinition()->isCreatable())) { $message = 'DataContainer ' . $definition->getName() . ' is closed'; - $environment->getEventDispatcher()->dispatch( + + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $dispatcher->dispatch( new LogEvent($message, 'ERROR', 'DC_General - edit()'), ContaoEvents::SYSTEM_LOG ); @@ -265,6 +294,7 @@ protected function ensurePropertyExists($property, $propertyDefinitions) protected function processInput($widgetManager) { $input = $this->getEnvironment()->getInputProvider(); + assert($input instanceof InputProviderInterface); if ($input->getValue('FORM_SUBMIT') === $this->getDataDefinition()->getName()) { $propertyValues = new PropertyValueBag(); @@ -295,11 +325,15 @@ protected function processInput($widgetManager) protected function handlePrePersist() { $environment = $this->getEnvironment(); + if (null !== $this->preFunction) { \call_user_func($this->preFunction, $environment, $this->model, $this->originalModel); } - $environment->getEventDispatcher()->dispatch( + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $dispatcher->dispatch( new PrePersistModelEvent($environment, $this->model, $this->originalModel), PrePersistModelEvent::NAME, ); @@ -313,12 +347,16 @@ protected function handlePrePersist() protected function handlePostPersist() { $environment = $this->getEnvironment(); + if (null !== $this->postFunction) { \call_user_func($this->postFunction, $environment, $this->model, $this->originalModel); } + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + $event = new PostPersistModelEvent($environment, $this->model, $this->originalModel); - $environment->getEventDispatcher()->dispatch($event, $event::NAME); + $dispatcher->dispatch($event, $event::NAME); } /** @@ -336,6 +374,8 @@ protected function handlePostPersist() protected function getButtonLabel($buttonLabel) { $translator = $this->getEnvironment()->getTranslator(); + assert($translator instanceof TranslatorInterface); + if (($label = $translator->translate($buttonLabel, $this->getDataDefinition()->getName())) !== $buttonLabel) { return $label; } @@ -358,6 +398,9 @@ protected function getButtonLabel($buttonLabel) */ protected function getEditButtons() { + $inputProvider = $this->getEnvironment()->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + $buttons = []; $basicDefinition = $this->getDataDefinition()->getBasicDefinition(); @@ -377,7 +420,7 @@ protected function getEditButtons() ); $buttons['save'] = $buttonTemplate->parse(); - if (!$this->getEnvironment()->getInputProvider()->getParameter('nb')) { + if (!$inputProvider->getParameter('nb')) { $buttonTemplate->setData( [ 'label' => $this->getButtonLabel('saveNclose'), @@ -395,7 +438,7 @@ protected function getEditButtons() if ( $basicDefinition->isCreatable() - && !$this->getEnvironment()->getInputProvider()->getParameter('nc') + && !$inputProvider->getParameter('nc') ) { $buttonTemplate->setData( [ @@ -412,7 +455,7 @@ protected function getEditButtons() $buttons['saveNcreate'] = $buttonTemplate->parse(); } - if ($this->getEnvironment()->getInputProvider()->hasParameter('s2e')) { + if ($inputProvider->hasParameter('s2e')) { $buttonTemplate->setData( [ 'label' => $this->getButtonLabel('saveNedit'), @@ -452,7 +495,10 @@ protected function getEditButtons() $event = new GetEditModeButtonsEvent($this->getEnvironment()); $event->setButtons($buttons); - $this->getEnvironment()->getEventDispatcher()->dispatch($event, $event::NAME); + $dispatcher = $this->getEnvironment()->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $dispatcher->dispatch($event, $event::NAME); $submitButtons = ['toggleIcon' => Image::getHtml('navcol.svg')]; $editButtons = $event->getButtons(); @@ -474,9 +520,9 @@ protected function getEditButtons() /** * Build the field sets. * - * @param ContaoWidgetManager $widgetManager The widget manager in use. - * @param PaletteInterface $palette The palette to use. - * @param PropertyValueBag $propertyValues The property values. + * @param ContaoWidgetManager $widgetManager The widget manager in use. + * @param PaletteInterface $palette The palette to use. + * @param PropertyValueBag|null $propertyValues The property values. * * @return array * @@ -485,13 +531,21 @@ protected function getEditButtons() */ protected function buildFieldSet($widgetManager, $palette, $propertyValues) { - $environment = $this->getEnvironment(); - $definition = $this->getDataDefinition(); - $translator = $environment->getTranslator(); + $environment = $this->getEnvironment(); + $definition = $this->getDataDefinition(); + + $translator = $environment->getTranslator(); + assert($translator instanceof TranslatorInterface); + + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + $propertyDefinitions = $definition->getPropertiesDefinition(); - $isAutoSubmit = ('auto' === $environment->getInputProvider()->getValue('SUBMIT_TYPE')); + $isAutoSubmit = ('auto' === $inputProvider->getValue('SUBMIT_TYPE')); $legendStates = $this->getLegendStates(); - $editInformation = System::getContainer()->get('cca.dc-general.edit-information'); + + $editInformation = System::getContainer()->get('cca.dc-general.edit-information'); + assert($editInformation instanceof EditInformationInterface); $fieldSets = []; $first = true; @@ -501,6 +555,7 @@ protected function buildFieldSet($widgetManager, $palette, $propertyValues) $definition->getName() ); $fields = []; + $fieldSet = []; $properties = $legend->getProperties($this->model, $propertyValues); if (!$properties) { @@ -534,7 +589,7 @@ protected function buildFieldSet($widgetManager, $palette, $propertyValues) if (!$property->isEditable($this->model, $propertyValues, $legend)) { $propertyDefinition = $propertyDefinitions->getProperty($property->getName()); $propertyDefinition->setExtra( - array_merge(($propertyDefinition->getExtra() ?? []), ['readonly' => true]) + \array_merge(($propertyDefinition->getExtra()), ['readonly' => true]) ); } @@ -571,10 +626,13 @@ protected function buildFieldSet($widgetManager, $palette, $propertyValues) */ protected function storeVersion(ModelInterface $model) { - $modelId = $model->getId(); - $environment = $this->getEnvironment(); - $definition = $this->getDataDefinition(); - $dataProvider = $environment->getDataProvider($model->getProviderName()); + $modelId = $model->getId(); + $environment = $this->getEnvironment(); + $definition = $this->getDataDefinition(); + + $dataProvider = $environment->getDataProvider($model->getProviderName()); + assert($dataProvider instanceof DataProviderInterface); + $dataProviderDefinition = $definition->getDataProviderDefinition(); $dataProviderInformation = $dataProviderDefinition->getInformation($model->getProviderName()); @@ -584,13 +642,18 @@ protected function storeVersion(ModelInterface $model) // Compare version and current record. $currentVersion = $dataProvider->getActiveVersion($modelId); + $model = $dataProvider->getVersion($modelId, $currentVersion); + assert($model instanceof ModelInterface); if ( !$currentVersion - || !$dataProvider->sameModels($model, $dataProvider->getVersion($modelId, $currentVersion)) + || !$dataProvider->sameModels($model, $model) ) { $user = BackendUser::getInstance(); - $dataProvider->saveVersion($model, $user->username); + $username = $user->username; + assert(\is_string($username)); + + $dataProvider->saveVersion($model, $username); } } @@ -618,9 +681,13 @@ protected function getManualSortingProperty() */ protected function handleSubmit(ModelInterface $model) { - $environment = $this->getEnvironment(); - $dispatcher = $environment->getEventDispatcher(); + $environment = $this->getEnvironment(); + + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); if ($inputProvider->hasValue('save')) { $newUrlEvent = new AddToUrlEvent('act=edit&btn=s&id=' . ModelId::fromModel($model)->getSerialized()); @@ -645,7 +712,10 @@ protected function handleSubmit(ModelInterface $model) } elseif ($inputProvider->hasValue('saveNback')) { $this->clearBackendStates(); - $parentProviderName = $environment->getDataDefinition()->getBasicDefinition()->getParentDataProvider(); + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $parentProviderName = $definition->getBasicDefinition()->getParentDataProvider(); $newUrlEvent = new GetReferrerEvent(false, $parentProviderName); $dispatcher->dispatch($newUrlEvent, ContaoEvents::SYSTEM_GET_REFERRER); @@ -656,7 +726,7 @@ protected function handleSubmit(ModelInterface $model) /** * Determine the headline to use. * - * @return string. + * @return string * * @deprecated This is deprecated since 2.3 and will be removed in 3.0. */ @@ -672,7 +742,7 @@ protected function getHeadline(): string /** * Determine the headline to use. * - * @return string. + * @return string */ protected function getSubHeadline(): string { @@ -680,9 +750,12 @@ protected function getSubHeadline(): string $event = new GetEditMaskSubHeadlineEvent($this->environment, $this->model); - $environment->getEventDispatcher()->dispatch($event, $event::NAME); + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $dispatcher->dispatch($event, $event::NAME); - return $event->getHeadline(); + return (string) $event->getHeadline(); } /** @@ -692,12 +765,17 @@ protected function getSubHeadline(): string * * @SuppressWarnings(PHPMD.Superglobals) * @SuppressWarnings(PHPMD.CyclomaticComplexity) + * @SuppressWarnings(PHPMD.LongVariable) */ protected function doPersist() { - $environment = $this->getEnvironment(); - $dataProvider = $environment->getDataProvider($this->model->getProviderName()); + $environment = $this->getEnvironment(); + + $dataProvider = $environment->getDataProvider($this->model->getProviderName()); + assert($dataProvider instanceof DataProviderInterface); + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); if (!$this->model->getMeta(ModelInterface::IS_CHANGED)) { return true; @@ -714,38 +792,49 @@ protected function doPersist() $models->push($this->model); $controller = $environment->getController(); + assert($controller instanceof ControllerInterface); + + $manualSortingProperty = $this->getManualSortingProperty(); + assert(\is_string($manualSortingProperty)); if ($inputProvider->hasParameter('after')) { $after = ModelId::fromSerialized($inputProvider->getParameter('after')); $previousDataProvider = $environment->getDataProvider($after->getDataProviderName()); + assert($previousDataProvider instanceof DataProviderInterface); + $previousFetchConfig = $previousDataProvider->getEmptyConfig(); $previousFetchConfig->setId($after->getId()); $previousModel = $previousDataProvider->fetch($previousFetchConfig); if ($previousModel) { - $controller->pasteAfter($previousModel, $models, $this->getManualSortingProperty()); + $controller->pasteAfter($previousModel, $models, $manualSortingProperty); } else { - $controller->pasteTop($models, $this->getManualSortingProperty()); + $controller->pasteTop($models, $manualSortingProperty); } } elseif ($inputProvider->hasParameter('into')) { $into = ModelId::fromSerialized($inputProvider->getParameter('into')); $parentDataProvider = $environment->getDataProvider($into->getDataProviderName()); + assert($parentDataProvider instanceof DataProviderInterface); + $parentFetchConfig = $parentDataProvider->getEmptyConfig(); $parentFetchConfig->setId($into->getId()); $parentModel = $parentDataProvider->fetch($parentFetchConfig); if ($parentModel) { - $controller->pasteInto($parentModel, $models, $this->getManualSortingProperty()); + $controller->pasteInto($parentModel, $models, $manualSortingProperty); } else { - $controller->pasteTop($models, $this->getManualSortingProperty()); + $controller->pasteTop($models, $manualSortingProperty); } } else { - $controller->pasteTop($models, $this->getManualSortingProperty()); + $controller->pasteTop($models, $manualSortingProperty); } - $environment->getClipboard()->clear()->saveTo($environment); + $clipboard = $environment->getClipboard(); + assert($clipboard instanceof ClipboardInterface); + + $clipboard->clear()->saveTo($environment); } else { if (!$this->allValuesUnique()) { return false; @@ -772,10 +861,16 @@ protected function doPersist() protected function allValuesUnique() { // Init some vars. - $environment = $this->getEnvironment(); - $translator = $environment->getTranslator(); - $dataProvider = $environment->getDataProvider($this->model->getProviderName()); + $environment = $this->getEnvironment(); + + $translator = $environment->getTranslator(); + assert($translator instanceof TranslatorInterface); + + $dataProvider = $environment->getDataProvider($this->model->getProviderName()); + assert($dataProvider instanceof DataProviderInterface); + $editInformation = System::getContainer()->get('cca.dc-general.edit-information'); + assert($editInformation instanceof EditInformationInterface); // Run each and check the unique flag. foreach ($this->getDataDefinition()->getPropertiesDefinition()->getPropertyNames() as $propertyName) { @@ -812,77 +907,102 @@ protected function allValuesUnique() * * @SuppressWarnings(PHPMD.LongVariable) * @SuppressWarnings(PHPMD.Superglobals) + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ public function execute() { - $environment = $this->getEnvironment(); - $definition = $this->getDataDefinition(); - $dataProvider = $environment->getDataProvider($this->model->getProviderName()); + $environment = $this->getEnvironment(); + $definition = $this->getDataDefinition(); + + $controller = $environment->getController(); + assert($controller instanceof ControllerInterface); + + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $model = $this->model; + assert($model instanceof ModelInterface); + + $dataProvider = $environment->getDataProvider($model->getProviderName()); + assert($dataProvider instanceof DataProviderInterface); + $dataProviderDefinition = $definition->getDataProviderDefinition(); - $dataProviderInformation = $dataProviderDefinition->getInformation($this->model->getProviderName()); - $inputProvider = $environment->getInputProvider(); - $palettesDefinition = $definition->getPalettesDefinition(); - $submitted = ($definition->getName() === $inputProvider->getValue('FORM_SUBMIT')); - $isAutoSubmit = ('auto' === $inputProvider->getValue('SUBMIT_TYPE')); - $editInformation = System::getContainer()->get('cca.dc-general.edit-information'); + $dataProviderInformation = $dataProviderDefinition->getInformation($model->getProviderName()); + + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + + $palettesDefinition = $definition->getPalettesDefinition(); + $submitted = ($definition->getName() === $inputProvider->getValue('FORM_SUBMIT')); + $isAutoSubmit = ('auto' === $inputProvider->getValue('SUBMIT_TYPE')); - $widgetManager = new ContaoWidgetManager($environment, $this->model); + $editInformation = System::getContainer()->get('cca.dc-general.edit-information'); + assert($editInformation instanceof EditInformationInterface); - $this->checkEditable($this->model); - $this->checkCreatable($this->model); + $widgetManager = new ContaoWidgetManager($environment, $model); - $environment->getEventDispatcher()->dispatch( - new PreEditModelEvent($environment, $this->model), + $this->checkEditable($model); + $this->checkCreatable($model); + + $dispatcher->dispatch( + new PreEditModelEvent($environment, $model), PreEditModelEvent::NAME ); - $environment->getEventDispatcher()->dispatch( - new EnforceModelRelationshipEvent($this->getEnvironment(), $this->model), + $dispatcher->dispatch( + new EnforceModelRelationshipEvent($this->getEnvironment(), $model), DcGeneralEvents::ENFORCE_MODEL_RELATIONSHIP ); // Pass 1: Get the palette for the values stored in the model. - $palette = $palettesDefinition->findPalette($this->model); + $palette = $palettesDefinition->findPalette($model); // Check if input mask has visible properties. - if (!\count($palette->getProperties($this->model))) { + if (!\count($palette->getProperties($model))) { // @codingStandardsIgnoreStart \trigger_error('No visible properties for this edit mask defined!', E_USER_ERROR); // @codingStandardsIgnoreEnd } $propertyValues = $this->processInput($widgetManager); + if ($submitted && $propertyValues) { // Pass 2: Determine the real palette we want to work on if we have some data submitted. - $palette = $palettesDefinition->findPalette($this->model, $propertyValues); + $palette = $palettesDefinition->findPalette($model, $propertyValues); // Update the model - the model might add some more errors to the propertyValueBag via exceptions. - $this->getEnvironment()->getController()->updateModelFromPropertyBag($this->model, $propertyValues); + $controller->updateModelFromPropertyBag($model, $propertyValues); } $fieldSets = $this->buildFieldSet($widgetManager, $palette, $propertyValues); - if ((!$isAutoSubmit) && $submitted && !$editInformation->getModelError($this->model)) { + if ((!$isAutoSubmit) && $submitted && !$editInformation->getModelError($model)) { if ($this->doPersist()) { - $this->handleSubmit($this->model); + $this->handleSubmit($model); } } + $errors = null; + if ($editInformation instanceof DefaultEditInformation) { + $errors = $editInformation->getFlatModelErrors($model); + } + $viewTemplate = new ContaoBackendViewTemplate('dcbe_general_edit'); $viewTemplate->setData( [ 'fieldsets' => $fieldSets, 'versions' => $dataProviderInformation->isVersioningEnabled() ? $dataProvider->getVersions( - $this->model->getId() + $model->getId() ) : null, 'subHeadline' => $this->getSubHeadline(), 'table' => $definition->getName(), 'enctype' => 'multipart/form-data', - 'error' => $editInformation->getFlatModelErrors($this->model), + 'error' => $errors, 'editButtons' => $this->getEditButtons(), 'noReload' => $editInformation->hasAnyModelError(), 'breadcrumb' => $this->breadcrumb, - 'model' => $this->model + 'model' => $model ] ); @@ -900,26 +1020,35 @@ public function execute() */ private function executeMultiLanguage(ContaoBackendViewTemplate $template) { + $dataProvider = $this->getEnvironment()->getDataProvider($this->model->getProviderName()); + assert($dataProvider instanceof DataProviderInterface); + if ( \in_array( MultiLanguageDataProviderInterface::class, - \class_implements( - $this->getEnvironment()->getDataProvider( - $this->model->getProviderName() - ) - ) + \class_implements($dataProvider) ) ) { /** @var MultiLanguageDataProviderInterface $dataProvider */ $dataProvider = $this->getEnvironment()->getDataProvider(); - $languages = System::getLanguages(); + + $locales = System::getContainer()->get('contao.intl.locales'); + assert($locales instanceof Locales); + + $languages = $locales->getLocales(null, true); + + $controller = $this->environment->getController(); + assert($controller instanceof ControllerInterface); + + $translator = $this->environment->getTranslator(); + assert($translator instanceof TranslatorInterface); $template->set( 'languages', - $this->getEnvironment()->getController()->getSupportedLanguages($this->model->getId()) + $controller->getSupportedLanguages($this->model->getId()) ) ->set('language', $dataProvider->getCurrentLanguage()) - ->set('languageSubmit', $this->environment->getTranslator()->translate('MSC.showSelected')) + ->set('languageSubmit', $translator->translate('MSC.showSelected')) ->set('languageHeadline', $languages[$dataProvider->getCurrentLanguage()] ?? ''); return; @@ -940,7 +1069,7 @@ private function executeMultiLanguage(ContaoBackendViewTemplate $template) */ protected function clearBackendStates() { - \setcookie('BE_PAGE_OFFSET', 0, 0, '/'); + \setcookie('BE_PAGE_OFFSET', '', 0, '/'); $_SESSION['TL_INFO'] = []; $_SESSION['TL_ERROR'] = []; @@ -971,19 +1100,21 @@ private function isLegendVisible($legend, $legendStates) */ private function getLegendStates() { - $environment = $this->getEnvironment(); - $definition = $environment->getDataDefinition(); - $legendStates = $environment->getSessionStorage()->get('LEGENDS') ?: []; + $environment = $this->getEnvironment(); - if (\array_key_exists($definition->getName(), $legendStates)) { - $legendStates = $legendStates[$definition->getName()]; + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); - return $legendStates; - } + $sessionStorage = $environment->getSessionStorage(); + assert($sessionStorage instanceof SessionStorageInterface); + + $legendStates = $sessionStorage->get('LEGENDS') ?: []; - $legendStates = []; + if (\array_key_exists($definition->getName(), $legendStates)) { + return $legendStates[$definition->getName()]; + } - return $legendStates; + return []; } /** diff --git a/src/Contao/View/Contao2BackendView/Event/BaseButtonEvent.php b/src/Contao/View/Contao2BackendView/Event/BaseButtonEvent.php index b96695ea..50962ccf 100644 --- a/src/Contao/View/Contao2BackendView/Event/BaseButtonEvent.php +++ b/src/Contao/View/Contao2BackendView/Event/BaseButtonEvent.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -32,6 +33,8 @@ class BaseButtonEvent extends AbstractEnvironmentAwareEvent { /** * The name of the event. + * + * @var string */ public const NAME = 'dc-general.view.contao2backend.button'; @@ -40,35 +43,35 @@ class BaseButtonEvent extends AbstractEnvironmentAwareEvent * * @var string */ - protected $attributes; + protected $attributes = ''; /** * The Html code to use for this button. * - * @var string + * @var string|null */ - protected $html; + protected $html = null; /** * The key/name of the button. * * @var string */ - protected $key; + protected $key = ''; /** * The label to use for the button. * * @var string */ - protected $label; + protected $label = ''; /** * The title to use for the button. * * @var string */ - protected $title; + protected $title = ''; /** * Set the HTML attributes for the button. @@ -113,7 +116,7 @@ public function setHtml($html) /** * Get the HTML code for the button. * - * @return string + * @return string|null */ public function getHtml() { diff --git a/src/Contao/View/Contao2BackendView/Event/BaseGetButtonsEvent.php b/src/Contao/View/Contao2BackendView/Event/BaseGetButtonsEvent.php index 42f54c8b..7c4d7cca 100644 --- a/src/Contao/View/Contao2BackendView/Event/BaseGetButtonsEvent.php +++ b/src/Contao/View/Contao2BackendView/Event/BaseGetButtonsEvent.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -33,6 +34,8 @@ class BaseGetButtonsEvent extends AbstractEnvironmentAwareEvent { /** * The name of the event. + * + * @var string */ public const NAME = 'dc-general.view.contao2backend.get-buttons'; @@ -41,7 +44,7 @@ class BaseGetButtonsEvent extends AbstractEnvironmentAwareEvent * * @var string[] */ - protected $buttons; + protected $buttons = []; /** * Set the list of buttons. diff --git a/src/Contao/View/Contao2BackendView/Event/BuildWidgetEvent.php b/src/Contao/View/Contao2BackendView/Event/BuildWidgetEvent.php index d28e7bc8..b4097c5d 100644 --- a/src/Contao/View/Contao2BackendView/Event/BuildWidgetEvent.php +++ b/src/Contao/View/Contao2BackendView/Event/BuildWidgetEvent.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author David Molineus * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -49,9 +50,9 @@ class BuildWidgetEvent extends AbstractModelAwareEvent /** * The instantiated widget. * - * @var Widget + * @var Widget|null */ - protected $widget; + protected $widget = null; /** * Create a new event. @@ -87,7 +88,7 @@ public function setWidget($widget) /** * Retrieve the widget instance from the event. * - * @return Widget + * @return Widget|null */ public function getWidget() { diff --git a/src/Contao/View/Contao2BackendView/Event/DecodePropertyValueForWidgetEvent.php b/src/Contao/View/Contao2BackendView/Event/DecodePropertyValueForWidgetEvent.php index 861d1ce1..4e579940 100644 --- a/src/Contao/View/Contao2BackendView/Event/DecodePropertyValueForWidgetEvent.php +++ b/src/Contao/View/Contao2BackendView/Event/DecodePropertyValueForWidgetEvent.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,8 @@ * @package contao-community-alliance/dc-general * @author Christian Schiffler * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -38,7 +39,7 @@ class DecodePropertyValueForWidgetEvent extends AbstractModelAwareEvent * * @var string */ - protected $property; + protected $property = ''; /** * The value of the data. @@ -58,10 +59,6 @@ class DecodePropertyValueForWidgetEvent extends AbstractModelAwareEvent */ public function setProperty($property) { - if (!\is_string($property)) { - throw new DcGeneralInvalidArgumentException('Only property name as string allowed.'); - } - $this->property = $property; return $this; diff --git a/src/Contao/View/Contao2BackendView/Event/EncodePropertyValueFromWidgetEvent.php b/src/Contao/View/Contao2BackendView/Event/EncodePropertyValueFromWidgetEvent.php index 217f6d47..e320471b 100644 --- a/src/Contao/View/Contao2BackendView/Event/EncodePropertyValueFromWidgetEvent.php +++ b/src/Contao/View/Contao2BackendView/Event/EncodePropertyValueFromWidgetEvent.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,8 @@ * @package contao-community-alliance/dc-general * @author Christian Schiffler * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -40,7 +41,7 @@ class EncodePropertyValueFromWidgetEvent extends AbstractModelAwareEvent * * @var string */ - protected $property; + protected $property = ''; /** * The value of the data. diff --git a/src/Contao/View/Contao2BackendView/Event/GetBreadcrumbEvent.php b/src/Contao/View/Contao2BackendView/Event/GetBreadcrumbEvent.php index 794aff5b..7b06d188 100644 --- a/src/Contao/View/Contao2BackendView/Event/GetBreadcrumbEvent.php +++ b/src/Contao/View/Contao2BackendView/Event/GetBreadcrumbEvent.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -37,7 +38,7 @@ class GetBreadcrumbEvent extends AbstractEnvironmentAwareEvent * * @var array */ - protected $elements; + protected $elements = []; /** * Set the breadcrumb elements to be displayed in the backend. diff --git a/src/Contao/View/Contao2BackendView/Event/GetGlobalButtonEvent.php b/src/Contao/View/Contao2BackendView/Event/GetGlobalButtonEvent.php index cb5e8a91..7a7c8015 100644 --- a/src/Contao/View/Contao2BackendView/Event/GetGlobalButtonEvent.php +++ b/src/Contao/View/Contao2BackendView/Event/GetGlobalButtonEvent.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -37,21 +38,21 @@ class GetGlobalButtonEvent extends BaseButtonEvent * * @var string */ - protected $accessKey; + protected $accessKey = ''; /** * The css class to use. * * @var string */ - protected $class; + protected $class = ''; /** * The href to use. * * @var string */ - protected $href; + protected $href = ''; /** * Set the hotkey for the button. diff --git a/src/Contao/View/Contao2BackendView/Event/GetGroupHeaderEvent.php b/src/Contao/View/Contao2BackendView/Event/GetGroupHeaderEvent.php index d35a8578..933cc58d 100644 --- a/src/Contao/View/Contao2BackendView/Event/GetGroupHeaderEvent.php +++ b/src/Contao/View/Contao2BackendView/Event/GetGroupHeaderEvent.php @@ -65,7 +65,7 @@ class GetGroupHeaderEvent extends AbstractModelAwareEvent /** * The value to be rendered. * - * @var string + * @var string|null */ protected $value; @@ -75,7 +75,7 @@ class GetGroupHeaderEvent extends AbstractModelAwareEvent * @param EnvironmentInterface $environment The environment. * @param ModelInterface $model The model being used as group header. * @param string $propertyName The name of the property being rendered into the group header. - * @param mixed $propertyValue The value of the property being rendered into the group header. + * @param string|null $propertyValue The value of the property being rendered into the group header. * @param string $groupingMode The grouping mode currently active. * @param int $groupingLength The grouping length currently active. */ @@ -146,7 +146,7 @@ public function setValue($value) /** * Retrieve the value to use in the group header. * - * @return string + * @return string|null */ public function getValue() { diff --git a/src/Contao/View/Contao2BackendView/Event/GetOperationButtonEvent.php b/src/Contao/View/Contao2BackendView/Event/GetOperationButtonEvent.php index a356f19e..59e5d6ab 100644 --- a/src/Contao/View/Contao2BackendView/Event/GetOperationButtonEvent.php +++ b/src/Contao/View/Contao2BackendView/Event/GetOperationButtonEvent.php @@ -171,7 +171,7 @@ public function isCircularReference() /** * Set the next model in the list, succeeding the current model. * - * @param ModelInterface $next The successor. + * @param ModelInterface|null $next The successor. * * @return $this */ @@ -195,7 +195,7 @@ public function getNext() /** * Set the previous model in the list, preceding the current model. * - * @param ModelInterface $previous The id of the predecessor. + * @param ModelInterface|null $previous The id of the predecessor. * * @return $this */ diff --git a/src/Contao/View/Contao2BackendView/Event/GetParentHeaderEvent.php b/src/Contao/View/Contao2BackendView/Event/GetParentHeaderEvent.php index 24a68a78..defe94ef 100644 --- a/src/Contao/View/Contao2BackendView/Event/GetParentHeaderEvent.php +++ b/src/Contao/View/Contao2BackendView/Event/GetParentHeaderEvent.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -15,7 +15,8 @@ * @author Tristan Lins * @author David Molineus * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -38,7 +39,7 @@ class GetParentHeaderEvent extends AbstractModelAwareEvent * * @var array */ - protected $additional; + protected $additional = []; /** * Set the additional lines that shall be added to the header section. diff --git a/src/Contao/View/Contao2BackendView/Event/GetPasteButtonEvent.php b/src/Contao/View/Contao2BackendView/Event/GetPasteButtonEvent.php index c0e7a3c4..dd9858e5 100644 --- a/src/Contao/View/Contao2BackendView/Event/GetPasteButtonEvent.php +++ b/src/Contao/View/Contao2BackendView/Event/GetPasteButtonEvent.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -122,7 +123,7 @@ class GetPasteButtonEvent extends BaseButtonEvent */ public function setCircularReference($circularReference) { - $this->circularReference = (bool) $circularReference; + $this->circularReference = $circularReference; return $this; } diff --git a/src/Contao/View/Contao2BackendView/Event/GetPasteRootButtonEvent.php b/src/Contao/View/Contao2BackendView/Event/GetPasteRootButtonEvent.php index dc2b93b3..fefdde37 100644 --- a/src/Contao/View/Contao2BackendView/Event/GetPasteRootButtonEvent.php +++ b/src/Contao/View/Contao2BackendView/Event/GetPasteRootButtonEvent.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -35,14 +36,14 @@ class GetPasteRootButtonEvent extends BaseButtonEvent * * @var string */ - protected $href; + protected $href = ''; /** * Determinator if the paste button shall be disabled. * * @var bool */ - protected $pasteDisabled; + protected $pasteDisabled = false; /** * Set the href for the button. diff --git a/src/Contao/View/Contao2BackendView/Event/ModelToLabelEvent.php b/src/Contao/View/Contao2BackendView/Event/ModelToLabelEvent.php index c8c110dc..e860b027 100644 --- a/src/Contao/View/Contao2BackendView/Event/ModelToLabelEvent.php +++ b/src/Contao/View/Contao2BackendView/Event/ModelToLabelEvent.php @@ -46,20 +46,22 @@ class ModelToLabelEvent extends AbstractModelAwareEvent * The label information instance. * * @var ModelFormatterConfigInterface + * + * @psalm-suppress PropertyNotSetInConstructor */ protected $listLabel; /** * The arguments to use when building the label from the format string. * - * @var array + * @var array */ protected $args = []; /** * Set the arguments to use when generating the final string representation using the format string. * - * @param array $args The arguments. + * @param array $args The arguments. * * @return ModelToLabelEvent */ @@ -73,7 +75,7 @@ public function setArgs($args) /** * Retrieve the arguments to use when generating the final string representation using the format string. * - * @return array + * @return array */ public function getArgs() { diff --git a/src/Contao/View/Contao2BackendView/Event/ParentViewChildRecordEvent.php b/src/Contao/View/Contao2BackendView/Event/ParentViewChildRecordEvent.php index 8fb46f4a..6e746e7d 100644 --- a/src/Contao/View/Contao2BackendView/Event/ParentViewChildRecordEvent.php +++ b/src/Contao/View/Contao2BackendView/Event/ParentViewChildRecordEvent.php @@ -37,7 +37,7 @@ class ParentViewChildRecordEvent extends AbstractModelAwareEvent * * @var string */ - protected $html; + protected $html = ''; /** * Set the html code to use as child record. diff --git a/src/Contao/View/Contao2BackendView/Event/PrepareMultipleModelsActionEvent.php b/src/Contao/View/Contao2BackendView/Event/PrepareMultipleModelsActionEvent.php index f55148f3..9fe5e74c 100644 --- a/src/Contao/View/Contao2BackendView/Event/PrepareMultipleModelsActionEvent.php +++ b/src/Contao/View/Contao2BackendView/Event/PrepareMultipleModelsActionEvent.php @@ -39,24 +39,24 @@ class PrepareMultipleModelsActionEvent extends AbstractActionAwareEvent /** * The model ids. * - * @var ModelIdInterface[] + * @var list */ - private $modelIds; + private array $modelIds; /** * The submit action. * * @var string */ - private $submitAction; + private string $submitAction; /** * Create a new instance. * - * @param EnvironmentInterface $environment The environment. - * @param Action $action The called action. - * @param ModelIdInterface[] $modelIds The list of model ids being parsed. - * @param string $submitAction The submit action name. + * @param EnvironmentInterface $environment The environment. + * @param Action $action The called action. + * @param list $modelIds The list of model ids being parsed. + * @param string $submitAction The submit action name. */ public function __construct(EnvironmentInterface $environment, Action $action, array $modelIds, $submitAction) { @@ -69,7 +69,7 @@ public function __construct(EnvironmentInterface $environment, Action $action, a /** * Get modelIds. * - * @return ModelIdInterface[] + * @return list */ public function getModelIds() { @@ -79,7 +79,7 @@ public function getModelIds() /** * Set the model ids. * - * @param ModelIdInterface[] $modelIds The new model ids. + * @param list $modelIds The new model ids. * * @return $this */ diff --git a/src/Contao/View/Contao2BackendView/EventListener/BackButtonListener.php b/src/Contao/View/Contao2BackendView/EventListener/BackButtonListener.php index 81c1fe37..1faba1d2 100644 --- a/src/Contao/View/Contao2BackendView/EventListener/BackButtonListener.php +++ b/src/Contao/View/Contao2BackendView/EventListener/BackButtonListener.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2021 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,8 @@ * @package contao-community-alliance/dc-general * @author Christian Schiffler * @author Sven Baumann - * @copyright 2013-2021 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -24,7 +25,10 @@ use ContaoCommunityAlliance\Contao\Bindings\Events\System\GetReferrerEvent; use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminatorAwareTrait; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\GetGlobalButtonEvent; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; +use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; /** * This handles the back button event in list views. @@ -52,6 +56,7 @@ public function handle(GetGlobalButtonEvent $event) $environment = $event->getEnvironment(); $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); if ( !( @@ -82,15 +87,21 @@ public function handle(GetGlobalButtonEvent $event) */ private function getReferrerUrl(EnvironmentInterface $environment) { + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + $parent = $environment->getParentDataDefinition(); $event = new GetReferrerEvent( true, (null !== $parent) ? $parent->getName() - : $environment->getDataDefinition()->getName() + : $definition->getName() ); - $environment->getEventDispatcher()->dispatch($event, ContaoEvents::SYSTEM_GET_REFERRER); + $dispatcher->dispatch($event, ContaoEvents::SYSTEM_GET_REFERRER); return $event->getReferrerUrl(); } diff --git a/src/Contao/View/Contao2BackendView/EventListener/ColorPickerWizardListener.php b/src/Contao/View/Contao2BackendView/EventListener/ColorPickerWizardListener.php index 7237f44a..d075341a 100644 --- a/src/Contao/View/Contao2BackendView/EventListener/ColorPickerWizardListener.php +++ b/src/Contao/View/Contao2BackendView/EventListener/ColorPickerWizardListener.php @@ -29,6 +29,8 @@ use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\BuildWidgetEvent; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\Properties\PropertyInterface; use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; +use ContaoCommunityAlliance\Translator\TranslatorInterface; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; /** * Widget Builder to append color picker wizards to Contao backend widgets. @@ -89,8 +91,11 @@ public static function getWizard($propInfo, EnvironmentInterface $environment) $wizard = ''; $propExtra = $propInfo->getExtra(); - if (\is_array($propExtra) && \array_key_exists('colorpicker', $propExtra) && $propExtra['colorpicker']) { - $pickerText = $environment->getTranslator()->translate('MSC.colorpicker'); + $translator = $environment->getTranslator(); + assert($translator instanceof TranslatorInterface); + + if (\array_key_exists('colorpicker', $propExtra) && $propExtra['colorpicker']) { + $pickerText = $translator->translate('MSC.colorpicker'); $event = new GenerateHtmlEvent( 'pickcolor.svg', $pickerText, @@ -102,7 +107,10 @@ public static function getWizard($propInfo, EnvironmentInterface $environment) ) ); - $environment->getEventDispatcher()->dispatch($event, ContaoEvents::IMAGE_GET_HTML); + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $dispatcher->dispatch($event, ContaoEvents::IMAGE_GET_HTML); // Support single fields as well (see contao/core#5240) $strKey = $propExtra['multiple'] ? $propInfo->getName() . '_0' : $propInfo->getName(); @@ -112,7 +120,7 @@ public static function getWizard($propInfo, EnvironmentInterface $environment) 'id: "ctrl_%3$s", startColor: ((cl = $("ctrl_%3$s").value.hexToRgb(true)) ? cl : [255, 0, 0]),' . 'imgPath: "%4$s", onComplete: function(color) {$("ctrl_%3$s").value = color.hex.replace("#", "");}});' . '});', - $event->getHtml(), + $event->getHtml() ?? '', $propInfo->getName(), $strKey, 'assets/colorpicker/images/' diff --git a/src/Contao/View/Contao2BackendView/EventListener/CreateModelButtonListener.php b/src/Contao/View/Contao2BackendView/EventListener/CreateModelButtonListener.php index bba77a07..fd5d40e0 100644 --- a/src/Contao/View/Contao2BackendView/EventListener/CreateModelButtonListener.php +++ b/src/Contao/View/Contao2BackendView/EventListener/CreateModelButtonListener.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,18 +13,21 @@ * @package contao-community-alliance/dc-general * @author Christian Schiffler * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ namespace ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\EventListener; +use ContaoCommunityAlliance\DcGeneral\Clipboard\ClipboardInterface; use ContaoCommunityAlliance\DcGeneral\Clipboard\Filter; use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminatorAwareTrait; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\GetGlobalButtonEvent; use ContaoCommunityAlliance\DcGeneral\Data\ModelId; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\BasicDefinitionInterface; +use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; use ContaoCommunityAlliance\UrlBuilder\UrlBuilder; /** @@ -52,11 +55,18 @@ public function handle(GetGlobalButtonEvent $event) } $environment = $event->getEnvironment(); - $basicDefinition = $environment->getDataDefinition()->getBasicDefinition(); - $mode = $basicDefinition->getMode(); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $basicDefinition = $definition->getBasicDefinition(); + assert($basicDefinition instanceof BasicDefinitionInterface); + + $mode = $basicDefinition->getMode(); if (!$basicDefinition->isCreatable()) { $event->setHtml(''); + return; } @@ -65,21 +75,33 @@ public function handle(GetGlobalButtonEvent $event) || (BasicDefinitionInterface::MODE_HIERARCHICAL === $mode) ) { $filter = new Filter(); - $filter->andModelIsFromProvider($basicDefinition->getDataProvider()); + + $provider = $basicDefinition->getDataProvider(); + assert(\is_string($provider)); + + $filter->andModelIsFromProvider($provider); if ($parentProviderName = $basicDefinition->getParentDataProvider()) { $filter->andParentIsFromProvider($parentProviderName); } else { $filter->andHasNoParent(); } - if ($environment->getClipboard()->isNotEmpty($filter)) { + $clipboard = $environment->getClipboard(); + assert($clipboard instanceof ClipboardInterface); + + if ($clipboard->isNotEmpty($filter)) { $event->setHtml(''); + return; } } $url = UrlBuilder::fromUrl($event->getHref()); - if ($serializedPid = $environment->getInputProvider()->getParameter('pid')) { + + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + + if ($serializedPid = $inputProvider->getParameter('pid')) { $url->setQueryParameter('pid', ModelId::fromSerialized($serializedPid)->getSerialized()); } diff --git a/src/Contao/View/Contao2BackendView/EventListener/CreateSubHeadlineListener.php b/src/Contao/View/Contao2BackendView/EventListener/CreateSubHeadlineListener.php index 955ebabb..6665c7d1 100644 --- a/src/Contao/View/Contao2BackendView/EventListener/CreateSubHeadlineListener.php +++ b/src/Contao/View/Contao2BackendView/EventListener/CreateSubHeadlineListener.php @@ -20,6 +20,8 @@ namespace ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\EventListener; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\GetEditMaskSubHeadlineEvent; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; +use ContaoCommunityAlliance\Translator\TranslatorInterface; /** * This class handles events to handle sub-headline at input mask. @@ -53,9 +55,15 @@ public function __invoke(GetEditMaskSubHeadlineEvent $event): void */ private function createSubHeadline(string $status, GetEditMaskSubHeadlineEvent $event): void { - $environment = $event->getEnvironment(); - $definitionName = $environment->getDataDefinition()->getName(); - $translator = $environment->getTranslator(); + $environment = $event->getEnvironment(); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $definitionName = $definition->getName(); + + $translator = $environment->getTranslator(); + assert($translator instanceof TranslatorInterface); $headline = $translator->translate($status, $definitionName, [$event->getModel()->getId()]); diff --git a/src/Contao/View/Contao2BackendView/EventListener/SelectModeButtonsListener.php b/src/Contao/View/Contao2BackendView/EventListener/SelectModeButtonsListener.php index 96031e3d..f143c04c 100644 --- a/src/Contao/View/Contao2BackendView/EventListener/SelectModeButtonsListener.php +++ b/src/Contao/View/Contao2BackendView/EventListener/SelectModeButtonsListener.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -12,7 +12,7 @@ * * @package contao-community-alliance/dc-general * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -22,6 +22,8 @@ use Contao\StringUtil; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\GetSelectModeButtonsEvent; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\ViewHelpers; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; +use ContaoCommunityAlliance\Translator\TranslatorInterface; /** * This class handle for add the default buttons for the select mode. @@ -37,9 +39,15 @@ class SelectModeButtonsListener */ public function handleEvent(GetSelectModeButtonsEvent $event) { - $environment = $event->getEnvironment(); - $translator = $environment->getTranslator(); - $basicDefinition = $environment->getDataDefinition()->getBasicDefinition(); + $environment = $event->getEnvironment(); + + $translator = $environment->getTranslator(); + assert($translator instanceof TranslatorInterface); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $basicDefinition = $definition->getBasicDefinition(); $buttons = []; $confirmMessage = \htmlentities( diff --git a/src/Contao/View/Contao2BackendView/FileSelect.php b/src/Contao/View/Contao2BackendView/FileSelect.php index 72d677b3..b3bd27c9 100644 --- a/src/Contao/View/Contao2BackendView/FileSelect.php +++ b/src/Contao/View/Contao2BackendView/FileSelect.php @@ -37,12 +37,19 @@ use ContaoCommunityAlliance\DcGeneral\Contao\Callback\Callbacks; use ContaoCommunityAlliance\DcGeneral\Contao\Compatibility\DcCompat; use ContaoCommunityAlliance\DcGeneral\Contao\InputProvider; +use ContaoCommunityAlliance\DcGeneral\Data\DataProviderInterface; use ContaoCommunityAlliance\DcGeneral\Data\ModelId; use ContaoCommunityAlliance\DcGeneral\Data\ModelIdInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\DcGeneral; +use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; use ContaoCommunityAlliance\DcGeneral\Factory\DcGeneralFactory; +use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; +use ContaoCommunityAlliance\DcGeneral\SessionStorageInterface; use ContaoCommunityAlliance\Translator\Contao\LangArrayTranslator; use ContaoCommunityAlliance\Translator\TranslatorChain; +use ContaoCommunityAlliance\Translator\TranslatorInterface; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; /** * Class FileSelect. @@ -55,19 +62,12 @@ */ class FileSelect { - /** - * Current ajax object. - * - * @var object - */ - protected $objAjax; - /** * The DcGeneral Object. * - * @var DcGeneral + * @var DcGeneral|null */ - protected $itemContainer; + protected $itemContainer = null; /** * Initialize the controller. @@ -84,9 +84,11 @@ public function __construct() BackendUser::getInstance(); Config::getInstance(); Database::getInstance(); + /** @psalm-suppress DeprecatedMethod */ BackendUser::getInstance()->authenticate(); System::loadLanguageFile('default'); + /** @psalm-suppress DeprecatedMethod */ Backend::setStaticUrls(); } @@ -102,7 +104,8 @@ public function run() { $inputProvider = new InputProvider(); - $template = new BackendTemplate('be_picker'); + $template = new BackendTemplate('be_picker'); + /** @psalm-suppress UndefinedMagicPropertyAssignment */ $template->main = ''; $ajax = $this->runAjaxRequest(); @@ -111,7 +114,14 @@ public function run() $this->setupItemContainer($modelId); - $sessionStorage = $this->itemContainer->getEnvironment()->getSessionStorage(); + $itemContainer = $this->itemContainer; + assert($itemContainer instanceof DcGeneral); + + $environment = $itemContainer->getEnvironment(); + assert($environment instanceof EnvironmentInterface); + + $sessionStorage = $environment->getSessionStorage(); + assert($sessionStorage instanceof SessionStorageInterface); // Define the current ID. \define( @@ -122,17 +132,31 @@ public function run() $fileSelector = $this->prepareFileSelector($modelId, $ajax); - $template->main = $fileSelector->generate(); - $template->theme = Backend::getTheme(); - $template->base = Environment::get('base'); - $template->language = $GLOBALS['TL_LANGUAGE']; - $template->title = StringUtil::specialchars($GLOBALS['TL_LANG']['MSC']['treepicker']); - $template->charset = $GLOBALS['TL_CONFIG']['characterSet']; - $template->addSearch = $fileSelector->searchField; - $template->search = $GLOBALS['TL_LANG']['MSC']['search']; - $template->action = StringUtil::ampersand(Environment::get('request')); - $template->value = $sessionStorage->get('file_selector_search'); - $template->manager = $GLOBALS['TL_LANG']['MSC']['treepickerManager']; + /** @psalm-suppress UndefinedMagicPropertyAssignment */ + $template->main = $fileSelector->generate(); + /** @psalm-suppress UndefinedMagicPropertyAssignment */ + $template->theme = Backend::getTheme(); + /** @psalm-suppress UndefinedMagicPropertyAssignment */ + $template->base = Environment::get('base'); + /** @psalm-suppress UndefinedMagicPropertyAssignment */ + $template->language = $GLOBALS['TL_LANGUAGE']; + /** @psalm-suppress UndefinedMagicPropertyAssignment */ + $template->title = StringUtil::specialchars($GLOBALS['TL_LANG']['MSC']['treepicker']); + /** @psalm-suppress UndefinedMagicPropertyAssignment */ + $template->charset = $GLOBALS['TL_CONFIG']['characterSet']; + /** + * @psalm-suppress UndefinedMagicPropertyAssignment + * @psalm-suppress UndefinedMagicPropertyFetch + */ + $template->addSearch = $fileSelector->searchField; + /** @psalm-suppress UndefinedMagicPropertyAssignment */ + $template->search = $GLOBALS['TL_LANG']['MSC']['search']; + $template->action = StringUtil::ampersand(Environment::get('request')); + /** @psalm-suppress UndefinedMagicPropertyAssignment */ + $template->value = $sessionStorage->get('file_selector_search'); + /** @psalm-suppress UndefinedMagicPropertyAssignment */ + $template->manager = $GLOBALS['TL_LANG']['MSC']['treepickerManager']; + /** @psalm-suppress UndefinedMagicPropertyAssignment */ $template->managerHref = ''; if ( @@ -141,23 +165,31 @@ public function run() ) { Backend::addFilesBreadcrumb('tl_files_picker'); } + /** @psalm-suppress UndefinedMagicPropertyAssignment */ $template->breadcrumb = $GLOBALS['TL_DCA']['tl_files']['list']['sorting']['breadcrumb']; $user = BackendUser::getInstance(); // Add the manager link. + /** @psalm-suppress UndefinedMethod */ if ($user->hasAccess('files', 'modules')) { - $template->manager = $GLOBALS['TL_LANG']['MSC']['fileManager']; + /** @psalm-suppress UndefinedMagicPropertyAssignment */ + $template->manager = $GLOBALS['TL_LANG']['MSC']['fileManager']; + /** @psalm-suppress UndefinedMagicPropertyAssignment */ $template->managerHref = 'contao/main.php?do=files&popup=1'; } + /** @psalm-suppress UndefinedMethod */ if (Input::get('switch') && $user->hasAccess('page', 'modules')) { - $template->switch = $GLOBALS['TL_LANG']['MSC']['pagePicker']; + /** @psalm-suppress UndefinedMagicPropertyAssignment */ + $template->switch = $GLOBALS['TL_LANG']['MSC']['pagePicker']; + /** @psalm-suppress UndefinedMagicPropertyAssignment */ $template->switchHref = \str_replace('contao/file.php', 'contao/page.php', StringUtil::ampersand(Environment::get('request'))); } // Prevent debug output at all cost. $GLOBALS['TL_CONFIG']['debugMode'] = false; + /** @psalm-suppress DeprecatedMethod */ $template->output(); } @@ -173,7 +205,15 @@ private function getActiveModel(ModelIdInterface $modelId) if (!Database::getInstance()->tableExists($modelId->getDataProviderName())) { return null; } - $dataProvider = $this->itemContainer->getEnvironment()->getDataProvider(); + + $itemContainer = $this->itemContainer; + assert($itemContainer instanceof DcGeneral); + + $environment = $itemContainer->getEnvironment(); + assert($environment instanceof EnvironmentInterface); + + $dataProvider = $environment->getDataProvider(); + assert($dataProvider instanceof DataProviderInterface); return $dataProvider->fetch($dataProvider->getEmptyConfig()->setId($modelId->getId())); } @@ -184,13 +224,22 @@ private function getActiveModel(ModelIdInterface $modelId) * @param ModelIdInterface $modelId The model identifier. * @param Ajax $ajax The ajax request. * + * @psalm-suppress DeprecatedClass * @return FileSelector * * @SuppressWarnings(PHPMD.Superglobals) */ private function prepareFileSelector(ModelIdInterface $modelId, Ajax $ajax = null) { - $inputProvider = $this->itemContainer->getEnvironment()->getInputProvider(); + $itemContainer = $this->itemContainer; + assert($itemContainer instanceof DcGeneral); + + $environment = $itemContainer->getEnvironment(); + assert($environment instanceof EnvironmentInterface); + + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + $propertyName = $inputProvider->getParameter('field'); $information = (array) $GLOBALS['TL_DCA'][$modelId->getDataProviderName()]['fields'][$propertyName]; @@ -198,22 +247,29 @@ private function prepareFileSelector(ModelIdInterface $modelId, Ajax $ajax = nul $information['eval'] = []; } + $itemContainer = $this->itemContainer; + assert($itemContainer instanceof DcGeneral); + + $definition = $itemContainer->getEnvironment(); + assert($definition instanceof ContainerInterface); + // Merge with the information from the data container. - $property = $this - ->itemContainer - ->getEnvironment() - ->getDataDefinition() - ->getPropertiesDefinition() - ->getProperty($propertyName); + $property = $definition->getPropertiesDefinition()->getProperty($propertyName); $extra = $property->getExtra(); $information['eval'] = \array_merge($extra, (array) $information['eval']); - $this->session->set('filePickerRef', Environment::get('request')); + $sessionStorage = $environment->getSessionStorage(); + assert($sessionStorage instanceof SessionStorageInterface); + $sessionStorage->set('filePickerRef', Environment::get('request')); - $combat = new DcCompat($this->itemContainer->getEnvironment(), $this->getActiveModel($modelId), $propertyName); + $combat = new DcCompat($itemContainer->getEnvironment(), $this->getActiveModel($modelId), $propertyName); - /** @var FileSelector $fileSelector */ + /** + * @var FileSelector $fileSelector + * + * @psalm-suppress DeprecatedClass + */ $fileSelector = new $GLOBALS['BE_FFL']['fileSelector']( Widget::getAttributesFromDca( $information, @@ -247,7 +303,14 @@ private function prepareFileSelector(ModelIdInterface $modelId, Ajax $ajax = nul */ private function prepareValuesForFileSelector($propertyName, ModelIdInterface $modelId, DcCompat $combat) { - $inputProvider = $this->itemContainer->getEnvironment()->getInputProvider(); + $itemContainer = $this->itemContainer; + assert($itemContainer instanceof DcGeneral); + + $environment = $itemContainer->getEnvironment(); + assert($environment instanceof EnvironmentInterface); + + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); $fileSelectorValues = []; foreach (\array_filter(\explode(',', $inputProvider->getParameter('value'))) as $k => $v) { @@ -273,7 +336,7 @@ private function prepareValuesForFileSelector($propertyName, ModelIdInterface $m } /** - * Setup the item container. + * Set up the item container. * * @param ModelIdInterface $modelId The model identifier. * @@ -284,7 +347,11 @@ private function prepareValuesForFileSelector($propertyName, ModelIdInterface $m private function setupItemContainer(ModelIdInterface $modelId) { $dispatcher = System::getContainer()->get('event_dispatcher'); + assert($dispatcher instanceof EventDispatcherInterface); + $translator = new TranslatorChain(); + assert($translator instanceof TranslatorInterface); + $translator->add(new LangArrayTranslator($dispatcher)); $this->itemContainer = (new DcGeneralFactory()) diff --git a/src/Contao/View/Contao2BackendView/Filter/LanguageFilter.php b/src/Contao/View/Contao2BackendView/Filter/LanguageFilter.php index 7e05d27c..5554fba2 100644 --- a/src/Contao/View/Contao2BackendView/Filter/LanguageFilter.php +++ b/src/Contao/View/Contao2BackendView/Filter/LanguageFilter.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2021 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -16,7 +16,7 @@ * @author David Molineus * @author Sven Baumann * @author Ingolf Steinhardt - * @copyright 2013-2021 Contao Community Alliance. + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -26,16 +26,24 @@ use ContaoCommunityAlliance\Contao\Bindings\ContaoEvents; use ContaoCommunityAlliance\Contao\Bindings\Events\Controller\ReloadEvent; use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminator; +use ContaoCommunityAlliance\DcGeneral\Controller\ControllerInterface; +use ContaoCommunityAlliance\DcGeneral\Data\LanguageInformationInterface; use ContaoCommunityAlliance\DcGeneral\Data\ModelId; +use ContaoCommunityAlliance\DcGeneral\Data\ModelIdInterface; use ContaoCommunityAlliance\DcGeneral\Data\MultiLanguageDataProviderInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\DcGeneralEvents; use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; use ContaoCommunityAlliance\DcGeneral\Event\ActionEvent; use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; +use ContaoCommunityAlliance\DcGeneral\SessionStorageInterface; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\EventDispatcher\EventSubscriberInterface; /** * Handler class for handling the show events. + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class LanguageFilter implements EventSubscriberInterface { @@ -83,7 +91,7 @@ public function handleAction(ActionEvent $event) } /** - * Check if the data provider is multi language and prepare the data provider with the selected language. + * Check if the data provider is multi-language and prepare the data provider with the selected language. * * @param EnvironmentInterface $environment The environment. * @param bool $resetToFallback Flag if the language must be reset to the fallback. @@ -96,26 +104,45 @@ public function handleAction(ActionEvent $event) private function checkLanguage($environment, $resetToFallback) { $sessionStorage = $environment->getSessionStorage(); - $dataProvider = $environment->getDataProvider(); - $providerName = $environment->getDataDefinition()->getName(); - $modelId = $this->modelIdFromInput($environment->getInputProvider()); - $languages = $environment->getController()->getSupportedLanguages($modelId); + assert($sessionStorage instanceof SessionStorageInterface); + + $dataProvider = $environment->getDataProvider(); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $providerName = $definition->getName(); + + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + + $modelId = $this->modelIdFromInput($inputProvider); + + $controller = $environment->getController(); + assert($controller instanceof ControllerInterface); + + $languages = $controller->getSupportedLanguages($modelId); if (!$languages) { return; } - // Exit out when not a multi language provider. + // Exit out when not a multi-language provider. if (!($dataProvider instanceof MultiLanguageDataProviderInterface)) { return; } // If a new item, we MUST reset to the fallback as that is the first language that has to be stored // and set this language to session. + $session = []; if ((null === $modelId) && $resetToFallback) { - $dataProvider->setCurrentLanguage($dataProvider->getFallbackLanguage(null)->getLocale()); + $fallbackLanguage = $dataProvider->getFallbackLanguage(null); + assert($fallbackLanguage instanceof LanguageInformationInterface); + + $dataProvider->setCurrentLanguage($fallbackLanguage->getLocale()); $session['ml_support'][$providerName] = $dataProvider->getCurrentLanguage(); $sessionStorage->set('dc_general', $session); + return; } @@ -128,7 +155,10 @@ private function checkLanguage($environment, $resetToFallback) $currentLanguage = ($session['ml_support'][$providerName] ?? $GLOBALS['TL_LANGUAGE']); if (!\array_key_exists($currentLanguage, $languages)) { - $currentLanguage = $dataProvider->getFallbackLanguage($modelId)->getLocale(); + $fallbackLanguage = $dataProvider->getFallbackLanguage($modelId); + assert($fallbackLanguage instanceof LanguageInformationInterface); + + $currentLanguage = $fallbackLanguage->getLocale(); } $session['ml_support'][$providerName] = $currentLanguage; @@ -150,21 +180,33 @@ private function checkLanguage($environment, $resetToFallback) private function checkLanguageSubmit($environment, $languages) { $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); if ('language_switch' !== $inputProvider->getValue('FORM_SUBMIT')) { return; } // Get/Check the new language. + $session = []; if ( $inputProvider->hasValue('language') && \array_key_exists($inputProvider->getValue('language'), $languages) ) { - $session['ml_support'][$environment->getDataDefinition()->getName()] = $inputProvider->getValue('language'); - $environment->getSessionStorage()->set('dc_general', $session); + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $session['ml_support'][$definition->getName()] = $inputProvider->getValue('language'); + + $sessionStorage = $environment->getSessionStorage(); + assert($sessionStorage instanceof SessionStorageInterface); + + $sessionStorage->set('dc_general', $session); } - $environment->getEventDispatcher()->dispatch(new ReloadEvent(), ContaoEvents::CONTROLLER_RELOAD); + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $dispatcher->dispatch(new ReloadEvent(), ContaoEvents::CONTROLLER_RELOAD); } /** @@ -172,7 +214,7 @@ private function checkLanguageSubmit($environment, $languages) * * @param InputProviderInterface $inputProvider The input provider. * - * @return mixed|null + * @return ModelIdInterface|null */ private function modelIdFromInput(InputProviderInterface $inputProvider) { diff --git a/src/Contao/View/Contao2BackendView/GlobalButtonRenderer.php b/src/Contao/View/Contao2BackendView/GlobalButtonRenderer.php index 11f6e40d..129169d6 100644 --- a/src/Contao/View/Contao2BackendView/GlobalButtonRenderer.php +++ b/src/Contao/View/Contao2BackendView/GlobalButtonRenderer.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2022 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -15,7 +15,7 @@ * @author Ingolf Steinhardt * @author Sven Baumann * @author David Molineus - * @copyright 2013-2022 Contao Community Alliance. + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -28,6 +28,7 @@ use ContaoCommunityAlliance\DcGeneral\Contao\DataDefinition\Definition\Contao2BackendViewDefinitionInterface; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\GetGlobalButtonEvent; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\GetGlobalButtonsEvent; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\CommandInterface; use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; use ContaoCommunityAlliance\Translator\TranslatorInterface; @@ -67,8 +68,15 @@ class GlobalButtonRenderer public function __construct(EnvironmentInterface $environment) { $this->environment = $environment; - $this->dispatcher = $environment->getEventDispatcher(); - $this->translator = $environment->getTranslator(); + assert($environment instanceof EnvironmentInterface); + + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + $this->dispatcher = $dispatcher; + + $translator = $environment->getTranslator(); + assert($translator instanceof TranslatorInterface); + $this->translator = $translator; } /** @@ -78,17 +86,13 @@ public function __construct(EnvironmentInterface $environment) */ public function render() { - /** @var CommandInterface[] $commands */ - $commands = $this - ->environment - ->getDataDefinition() - ->getDefinition(Contao2BackendViewDefinitionInterface::NAME) - ->getGlobalCommands() - ->getCommands(); - - if (!\is_array($commands)) { - $commands = []; - } + $definition = $this->environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $backendView = $definition->getDefinition(Contao2BackendViewDefinitionInterface::NAME); + assert($backendView instanceof Contao2BackendViewDefinitionInterface); + + $commands = $backendView->getGlobalCommands()->getCommands(); $buttons = []; foreach ($commands as $command) { @@ -134,19 +138,19 @@ private function renderButton(CommandInterface $command) $href = $event->getUrl(); } - if (null === $label) { + if ('' === $label) { $label = $command->getName(); } $buttonEvent = new GetGlobalButtonEvent($this->environment); $buttonEvent - ->setAccessKey(isset($extra['accesskey']) ? \trim($extra['accesskey']) : null) + ->setAccessKey(isset($extra['accesskey']) ? \trim($extra['accesskey']) : '') ->setAttributes(' ' . \ltrim($extra['attributes'] ?? '')) ->setClass($extra['class']) ->setKey($command->getName()) ->setHref($href) ->setLabel($label) - ->setTitle($this->translate((string) $command->getDescription())); + ->setTitle($this->translate($command->getDescription())); $this->dispatcher->dispatch($buttonEvent, GetGlobalButtonEvent::NAME); // Allow to override the button entirely - if someone sets empty string, we keep it. @@ -174,7 +178,10 @@ private function renderButton(CommandInterface $command) */ private function translate($path) { - $value = $this->translator->translate($path, $this->environment->getDataDefinition()->getName()); + $definition = $this->environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $value = $this->translator->translate($path, $definition->getName()); if ($path !== $value) { return $value; } diff --git a/src/Contao/View/Contao2BackendView/PanelBuilder.php b/src/Contao/View/Contao2BackendView/PanelBuilder.php index b391f4e1..60e45f31 100644 --- a/src/Contao/View/Contao2BackendView/PanelBuilder.php +++ b/src/Contao/View/Contao2BackendView/PanelBuilder.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,8 @@ * @package contao-community-alliance/dc-general * @author Christian Schiffler * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -21,6 +22,7 @@ namespace ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView; use ContaoCommunityAlliance\DcGeneral\Contao\DataDefinition\Definition\Contao2BackendViewDefinitionInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\Panel\ElementInformationInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\Panel\FilterElementInformationInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\Panel\LimitElementInformationInterface; @@ -47,9 +49,9 @@ class PanelBuilder /** * The environment. * - * @var Contao2BackendViewDefinitionInterface + * @var EnvironmentInterface */ - private $environment; + private EnvironmentInterface $environment; /** * Create a new instance. @@ -71,15 +73,15 @@ public function build() $panel = new DefaultPanelContainer(); $panel->setEnvironment($this->environment); - /** @var Contao2BackendViewDefinitionInterface $viewDefinition */ - $viewDefinition = $this - ->environment - ->getDataDefinition() - ->getDefinition(Contao2BackendViewDefinitionInterface::NAME); + $definition = $this->environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $viewDefinition = $definition->getDefinition(Contao2BackendViewDefinitionInterface::NAME); + assert($viewDefinition instanceof Contao2BackendViewDefinitionInterface); foreach ($viewDefinition->getPanelLayout()->getRows() as $panelKey => $row) { $panelRow = new DefaultPanel(); - $panel->addPanel($panelKey, $panelRow); + $panel->addPanel((string) $panelKey, $panelRow); foreach ($row as $element) { /** @var ElementInformationInterface $element */ if (null !== $instance = $this->createElement($element)) { diff --git a/src/Contao/View/Contao2BackendView/PanelRenderer.php b/src/Contao/View/Contao2BackendView/PanelRenderer.php index 31ecae19..21c8246a 100644 --- a/src/Contao/View/Contao2BackendView/PanelRenderer.php +++ b/src/Contao/View/Contao2BackendView/PanelRenderer.php @@ -29,8 +29,11 @@ use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\GetPanelElementTemplateEvent; use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralRuntimeException; +use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; +use ContaoCommunityAlliance\DcGeneral\Panel\PanelContainerInterface; use ContaoCommunityAlliance\DcGeneral\Panel\PanelElementInterface; use ContaoCommunityAlliance\DcGeneral\Panel\PanelInterface; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; /** * This class renders a backend view panel including all elements. @@ -57,7 +60,7 @@ public function __construct($view) /** * Retrieve the environment. * - * @return EnvironmentInterface + * @return EnvironmentInterface|null */ protected function getEnvironment() { @@ -75,9 +78,13 @@ protected function getEnvironment() protected function renderPanelElement($element, $cssClass) { $environment = $this->getEnvironment(); + assert($environment instanceof EnvironmentInterface); + + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); $event = new GetPanelElementTemplateEvent($environment, $element); - $environment->getEventDispatcher()->dispatch($event, $event::NAME); + $dispatcher->dispatch($event, $event::NAME); $template = $event->getTemplate(); @@ -105,7 +112,7 @@ protected function isIgnoredPanel(PanelElementInterface $element, $ignoredPanels return false; } - foreach ((array) $ignoredPanels as $class) { + foreach ($ignoredPanels as $class) { if ($element instanceof $class) { return true; } @@ -173,9 +180,13 @@ protected function renderPanelRow($panel, $ignoredPanels) public function render($ignoredPanels = []) { $environment = $this->getEnvironment(); + assert($environment instanceof EnvironmentInterface); + + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); // If in edit/override all mode and list all properties, the panel filter isn´t in use. - if ('properties' === $environment->getInputProvider()->getParameter('select')) { + if ('properties' === $inputProvider->getParameter('select')) { return ''; } @@ -183,8 +194,11 @@ public function render($ignoredPanels = []) throw new DcGeneralRuntimeException('No panel information stored in data container.'); } + $panelContainer = $this->view->getPanel(); + assert($panelContainer instanceof PanelContainerInterface); + $panels = []; - foreach ($this->view->getPanel() as $panel) { + foreach ($panelContainer as $panel) { $panels[] = $this->renderPanelRow($panel, $ignoredPanels); } @@ -192,10 +206,13 @@ public function render($ignoredPanels = []) $template = new ContaoBackendViewTemplate('dcbe_general_panel'); $themeEvent = new GetThemeEvent(); - $environment->getEventDispatcher()->dispatch($themeEvent, ContaoEvents::BACKEND_GET_THEME); + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $dispatcher->dispatch($themeEvent, ContaoEvents::BACKEND_GET_THEME); $template - ->set('action', StringUtil::ampersand($environment->getInputProvider()->getRequestUrl(), true)) + ->set('action', StringUtil::ampersand($inputProvider->getRequestUrl(), true)) ->set('theme', $themeEvent->getTheme()) ->set('panel', $panels); diff --git a/src/Contao/View/Contao2BackendView/ParentView.php b/src/Contao/View/Contao2BackendView/ParentView.php index ea164a21..d8e03384 100644 --- a/src/Contao/View/Contao2BackendView/ParentView.php +++ b/src/Contao/View/Contao2BackendView/ParentView.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -16,13 +16,15 @@ * @author Tristan Lins * @author David Molineus * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ namespace ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView; +use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; use ContaoCommunityAlliance\DcGeneral\Event\EnforceModelRelationshipEvent; use ContaoCommunityAlliance\DcGeneral\EventListener\ModelRelationship\ParentEnforcingListener; @@ -42,7 +44,10 @@ class ParentView extends BaseView */ public function enforceModelRelationship($model) { + $environment = $this->getEnvironment(); + assert($environment instanceof EnvironmentInterface); + // Fallback implementation. - (new ParentEnforcingListener())->process(new EnforceModelRelationshipEvent($this->getEnvironment(), $model)); + (new ParentEnforcingListener())->process(new EnforceModelRelationshipEvent($environment, $model)); } } diff --git a/src/Contao/View/Contao2BackendView/Subscriber/CheckPermission.php b/src/Contao/View/Contao2BackendView/Subscriber/CheckPermission.php index 57588b04..89011350 100644 --- a/src/Contao/View/Contao2BackendView/Subscriber/CheckPermission.php +++ b/src/Contao/View/Contao2BackendView/Subscriber/CheckPermission.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,8 @@ * @package contao-community-alliance/dc-general * @author Sven Baumann * @author Christian Schiffler - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -126,7 +127,9 @@ public function checkPermissionIsEditable(BuildDataDefinitionEvent $event) return; } - $view = $container->getDefinition(Contao2BackendViewDefinitionInterface::NAME); + $view = $container->getDefinition(Contao2BackendViewDefinitionInterface::NAME); + assert($view instanceof Contao2BackendViewDefinitionInterface); + $modelCommands = $view->getModelCommands(); $this->disableCommandByActionName($modelCommands, 'edit'); @@ -153,7 +156,9 @@ public function checkPermissionIsDeletable(BuildDataDefinitionEvent $event) return; } - $view = $container->getDefinition(Contao2BackendViewDefinitionInterface::NAME); + $view = $container->getDefinition(Contao2BackendViewDefinitionInterface::NAME); + assert($view instanceof Contao2BackendViewDefinitionInterface); + $modelCommands = $view->getModelCommands(); $this->disableCommandByActionName($modelCommands, 'delete'); @@ -178,7 +183,9 @@ public function checkPermissionIsCreatable(BuildDataDefinitionEvent $event) return; } - $view = $container->getDefinition(Contao2BackendViewDefinitionInterface::NAME); + $view = $container->getDefinition(Contao2BackendViewDefinitionInterface::NAME); + assert($view instanceof Contao2BackendViewDefinitionInterface); + $modelCommands = $view->getModelCommands(); $this->disableCommandByActionName($modelCommands, 'copy'); @@ -210,7 +217,7 @@ private function getVisibilityConditionChain($property) /** * Disable command by action name. * - * @param CommandCollectionInterface $commands The commands collection. + * @param CommandCollectionInterface $commands The command's collection. * @param string $actionName The action name. * * @return void diff --git a/src/Contao/View/Contao2BackendView/Subscriber/GetGroupHeaderSubscriber.php b/src/Contao/View/Contao2BackendView/Subscriber/GetGroupHeaderSubscriber.php index 3528fac1..711f68fb 100644 --- a/src/Contao/View/Contao2BackendView/Subscriber/GetGroupHeaderSubscriber.php +++ b/src/Contao/View/Contao2BackendView/Subscriber/GetGroupHeaderSubscriber.php @@ -31,6 +31,7 @@ use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\GetGroupHeaderEvent; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\ViewHelpers; use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\Properties\PropertyInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\GroupAndSortingInformationInterface; use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; @@ -86,17 +87,18 @@ public function handle(GetGroupHeaderEvent $event) } $environment = $event->getEnvironment(); - $property = $environment - ->getDataDefinition() - ->getPropertiesDefinition() - ->getProperty($event->getGroupField()); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); // No property? Get out! - if (!$property) { + if (!$definition->getPropertiesDefinition()->hasProperty($event->getGroupField())) { $event->setValue('-'); return; } + $property = $definition->getPropertiesDefinition()->getProperty($event->getGroupField()); + $value = $this->formatGroupHeader( $environment, $event->getModel(), @@ -116,10 +118,10 @@ public function handle(GetGroupHeaderEvent $event) * @param EnvironmentInterface $environment The environment. * @param ModelInterface $model The model. * @param PropertyInterface $property The property. - * @param int $groupingMode The grouping mode. + * @param string $groupingMode The grouping mode. * @param int $groupingLength The grouping length. * - * @return string + * @return string|null */ protected function formatGroupHeader( EnvironmentInterface $environment, @@ -140,10 +142,10 @@ protected function formatGroupHeader( $value = ViewHelpers::getReadableFieldValue($environment, $property, $model); if (isset($evaluation['reference'])) { - $remoteNew = $evaluation['reference'][$value]; + $remoteNew = $evaluation['reference'][$value] ?? null; } elseif (ArrayUtil::isAssoc($property->getOptions())) { $options = $property->getOptions(); - $remoteNew = $options[$value]; + $remoteNew = $options[$value] ?? null; } else { $remoteNew = $value; } @@ -176,13 +178,13 @@ private function formatCheckboxOptionLabel($value) /** * Format the group header by the grouping mode. * - * @param int $groupingMode The grouping mode. + * @param string $groupingMode The grouping mode. * @param int $groupingLength The grouping length. * @param EnvironmentInterface $environment The environment. * @param PropertyInterface $property The current property definition. * @param ModelInterface $model The current data model. * - * @return string + * @return string|null */ private function formatByGroupingMode( $groupingMode, @@ -226,7 +228,7 @@ private function formatByCharGrouping($value, $groupingLength) return '-'; } - return mb_strtoupper(mb_substr($value, 0, $groupingLength ?: null)); + return \mb_strtoupper(\mb_substr($value, 0, $groupingLength ?: null)); } /** @@ -234,14 +236,16 @@ private function formatByCharGrouping($value, $groupingLength) * * @param int $value The value. * - * @return string + * @return string|null */ - private function formatByDayGrouping($value) + private function formatByDayGrouping(int $value): ?string { $value = $this->getTimestamp($value); - if ('' === $value) { + + if (0 === $value) { return '-'; } + $event = new ParseDateEvent($value, Config::get('dateFormat')); $this->dispatcher->dispatch($event, ContaoEvents::DATE_PARSE); @@ -253,12 +257,13 @@ private function formatByDayGrouping($value) * * @param int $value The value. * - * @return string + * @return string|null */ - private function formatByMonthGrouping($value) + private function formatByMonthGrouping(int $value): ?string { $value = $this->getTimestamp($value); - if ('' === $value) { + + if (0 === $value) { return '-'; } $event = new ParseDateEvent($value, 'F Y'); @@ -277,11 +282,12 @@ private function formatByMonthGrouping($value) private function formatByYearGrouping($value) { $value = $this->getTimestamp($value); - if ('' === $value) { + + if (0 === $value) { return '-'; } - return date('Y', $value); + return \date('Y', $value); } /** diff --git a/src/Contao/View/Contao2BackendView/Subscriber/MultipleHandlerSubscriber.php b/src/Contao/View/Contao2BackendView/Subscriber/MultipleHandlerSubscriber.php index 8e0164a4..87beaade 100644 --- a/src/Contao/View/Contao2BackendView/Subscriber/MultipleHandlerSubscriber.php +++ b/src/Contao/View/Contao2BackendView/Subscriber/MultipleHandlerSubscriber.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2021 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -12,26 +12,35 @@ * * @package contao-community-alliance/dc-general * @author Sven Baumann - * @copyright 2013-2021 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0 * @filesource */ namespace ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Subscriber; +use Contao\Widget; use ContaoCommunityAlliance\DcGeneral\Contao\DataDefinition\Definition\Contao2BackendViewDefinitionInterface; use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminator; use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminatorAwareTrait; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\BuildWidgetEvent; use ContaoCommunityAlliance\DcGeneral\Data\ModelId; use ContaoCommunityAlliance\DcGeneral\Data\ModelIdInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\DcGeneralEvents; use ContaoCommunityAlliance\DcGeneral\Event\ActionEvent; +use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralException; +use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; +use ContaoCommunityAlliance\DcGeneral\SessionStorageInterface; use MenAtWork\MultiColumnWizardBundle\Event\GetOptionsEvent; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\EventDispatcher\EventSubscriberInterface; /** * The multiple handler subscriber provides functions for edit/override all. + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class MultipleHandlerSubscriber implements EventSubscriberInterface { @@ -63,7 +72,7 @@ public function __construct(RequestScopeDeterminator $scopeDeterminator) * * array('eventName' => array('methodName', $priority)) * * array('eventName' => array(array('methodName1', $priority), array('methodName2'))) * - * @return array The event names to listen to + * @return array> */ public static function getSubscribedEvents() { @@ -96,7 +105,11 @@ public function prepareGlobalAllButton(ActionEvent $event) } $dataDefinition = $event->getEnvironment()->getDataDefinition(); - $backendView = $dataDefinition->getDefinition(Contao2BackendViewDefinitionInterface::NAME); + assert($dataDefinition instanceof ContainerInterface); + + $backendView = $dataDefinition->getDefinition(Contao2BackendViewDefinitionInterface::NAME); + assert($backendView instanceof Contao2BackendViewDefinitionInterface); + $globalCommands = $backendView->getGlobalCommands(); if ($globalCommands->hasCommandNamed('all')) { @@ -108,7 +121,7 @@ public function prepareGlobalAllButton(ActionEvent $event) } /** - * Deactivate global button their are not useful. + * Deactivate global button there are not useful. * * @param ActionEvent $event The event. * @@ -125,7 +138,11 @@ public function deactivateGlobalButton(ActionEvent $event) } $dataDefinition = $event->getEnvironment()->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + $backendView = $dataDefinition->getDefinition(Contao2BackendViewDefinitionInterface::NAME); + assert($backendView instanceof Contao2BackendViewDefinitionInterface); + $globalCommands = $backendView->getGlobalCommands(); $allowedButton = ['close_all_button']; @@ -153,10 +170,13 @@ public function handleOriginalOptions(GetOptionsEvent $event) { $environment = $event->getEnvironment(); + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + if ( - !$this->getScopeDeterminator()->currentScopeIsBackend() - || ('select' !== $environment->getInputProvider()->getParameter('act')) - || ('edit' !== $environment->getInputProvider()->getParameter('select')) + ('select' !== $inputProvider->getParameter('act')) + || ('edit' !== $inputProvider->getParameter('select')) + || !$this->getScopeDeterminator()->currentScopeIsBackend() ) { return; } @@ -184,7 +204,10 @@ public function handleOriginalOptions(GetOptionsEvent $event) $event->getOptions() ); - $environment->getEventDispatcher()->dispatch($originalOptionsEvent, GetOptionsEvent::NAME); + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $dispatcher->dispatch($originalOptionsEvent, GetOptionsEvent::NAME); $event->setOptions($originalOptionsEvent->getOptions()); @@ -202,15 +225,21 @@ public function handleOriginalWidget(BuildWidgetEvent $event) { $environment = $event->getEnvironment(); + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + if ( - !$this->getScopeDeterminator()->currentScopeIsBackend() - || ('select' !== $environment->getInputProvider()->getParameter('act')) - || ('edit' !== $environment->getInputProvider()->getParameter('select')) + 'select' !== $inputProvider->getParameter('act') + || 'edit' !== $inputProvider->getParameter('select') + || !$this->getScopeDeterminator()->currentScopeIsBackend() ) { return; } - $properties = $environment->getDataDefinition()->getPropertiesDefinition(); + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $properties = $definition->getPropertiesDefinition(); $this->findModelIdByPropertyName($event); @@ -218,10 +247,8 @@ public function handleOriginalWidget(BuildWidgetEvent $event) $modelId = ModelId::fromModel($model); $originalPropertyName = $this->getOriginalPropertyName($event->getProperty()->getName(), $modelId); - if ( - (null === $originalPropertyName) - || ((null !== $originalPropertyName) && (false === $properties->hasProperty($originalPropertyName))) - ) { + + if (null === $originalPropertyName) { return; } @@ -244,15 +271,22 @@ public function handleOriginalWidget(BuildWidgetEvent $event) $originalEvent = new BuildWidgetEvent($environment, $model, $originalProperty); - $environment->getEventDispatcher()->dispatch($originalEvent, BuildWidgetEvent::NAME); + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); - $originalEvent->getWidget()->id = $event->getProperty()->getName(); - $originalEvent->getWidget()->name = + $dispatcher->dispatch($originalEvent, BuildWidgetEvent::NAME); + + $widget = $originalEvent->getWidget(); + assert($widget instanceof Widget); + + $widget->id = $event->getProperty()->getName(); + $widget->name = \str_replace('::', '____', $modelId->getSerialized()) . '_[' . $originalPropertyName . ']'; - $originalEvent->getWidget()->tl_class = ''; + /** @psalm-suppress UndefinedMagicPropertyAssignment */ + $widget->tl_class = ''; - $event->setWidget($originalEvent->getWidget()); + $event->setWidget($widget); $originalProperty->setExtra($originalExtra); @@ -272,24 +306,35 @@ private function findModelIdByPropertyName(BuildWidgetEvent $event) return; } - $environment = $event->getEnvironment(); + $environment = $event->getEnvironment(); + $dataDefinition = $environment->getDataDefinition(); - $inputProvider = $environment->getInputProvider(); + assert($dataDefinition instanceof ContainerInterface); + + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + $sessionStorage = $environment->getSessionStorage(); + assert($sessionStorage instanceof SessionStorageInterface); + /** @var array{models: list} $session */ $session = $sessionStorage->get($dataDefinition->getName() . '.' . $inputProvider->getParameter('mode')); $model = null; foreach ($session['models'] as $sessionModel) { $model = $sessionModel; - if (0 !== \strpos($event->getProperty()->getName(), \str_replace('::', '____', $sessionModel))) { + if (!str_starts_with($event->getProperty()->getName(), \str_replace('::', '____', $sessionModel))) { continue; } break; } + if (null === $model) { + throw new DcGeneralException('Failed to find a model.'); + } + $modelId = ModelId::fromSerialized($model); $event->getModel()->setId($modelId->getId()); diff --git a/src/Contao/View/Contao2BackendView/Subscriber/RichTextFileUuidSubscriber.php b/src/Contao/View/Contao2BackendView/Subscriber/RichTextFileUuidSubscriber.php index 63d5d33d..d3fe8400 100644 --- a/src/Contao/View/Contao2BackendView/Subscriber/RichTextFileUuidSubscriber.php +++ b/src/Contao/View/Contao2BackendView/Subscriber/RichTextFileUuidSubscriber.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,8 @@ * @package contao-community-alliance/dc-general * @author Sven Baumann * @author Christian Schiffler - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -24,6 +25,7 @@ use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminator; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\DecodePropertyValueForWidgetEvent; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\EncodePropertyValueFromWidgetEvent; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use Symfony\Component\EventDispatcher\EventSubscriberInterface; /** @@ -64,7 +66,7 @@ public function __construct(RequestScopeDeterminator $scopeDeterminator) * * array('eventName' => array('methodName', $priority)) * * array('eventName' => array(array('methodName1', $priority), array('methodName2'))) * - * @return array The event names to listen to + * @return array> */ public static function getSubscribedEvents() { @@ -93,7 +95,10 @@ public function convertFileSourceToUuid(EncodePropertyValueFromWidgetEvent $even return; } - $propertiesDefinition = $event->getEnvironment()->getDataDefinition()->getPropertiesDefinition(); + $definition = $event->getEnvironment()->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $propertiesDefinition = $definition->getPropertiesDefinition(); $property = $propertiesDefinition->getProperty($event->getProperty()); @@ -120,7 +125,10 @@ public function convertUuidToFileSource(DecodePropertyValueForWidgetEvent $event return; } - $propertiesDefinition = $event->getEnvironment()->getDataDefinition()->getPropertiesDefinition(); + $definition = $event->getEnvironment()->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $propertiesDefinition = $definition->getPropertiesDefinition(); $property = $propertiesDefinition->getProperty($event->getProperty()); diff --git a/src/Contao/View/Contao2BackendView/Subscriber/WidgetBuilder.php b/src/Contao/View/Contao2BackendView/Subscriber/WidgetBuilder.php index 16be0f3e..64f09b90 100644 --- a/src/Contao/View/Contao2BackendView/Subscriber/WidgetBuilder.php +++ b/src/Contao/View/Contao2BackendView/Subscriber/WidgetBuilder.php @@ -24,7 +24,6 @@ namespace ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Subscriber; -use Contao\CheckBox; use Contao\StringUtil; use Contao\Widget; use ContaoCommunityAlliance\Contao\Bindings\ContaoEvents; @@ -43,16 +42,21 @@ use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Widget\PageTreeOrder; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Widget\TreePickerOrder; use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\Properties\PropertyInterface; use ContaoCommunityAlliance\DcGeneral\EnvironmentAwareInterface; use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralRuntimeException; +use ContaoCommunityAlliance\Translator\TranslatorInterface; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; /** * Widget Builder build Contao backend widgets. * * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * + * @final */ class WidgetBuilder implements EnvironmentAwareInterface { @@ -61,19 +65,21 @@ class WidgetBuilder implements EnvironmentAwareInterface * * @var RequestScopeDeterminator */ - private static $scopeDeterminator; + private static RequestScopeDeterminator $scopeDeterminator; /** * The environment. * * @var EnvironmentInterface */ - private $environment; + private EnvironmentInterface $environment; /** * Mapping list of widget types where the DC General has it own widgets. * * @var array + * + * @psalm-suppress DeprecatedClass - we know. :D */ protected static $widgetMapping = [ 'fileTree' => FileTree::class, @@ -112,8 +118,10 @@ public static function handleEvent(BuildWidgetEvent $event) return; } - $event - ->setWidget((new static($event->getEnvironment()))->buildWidget($event->getProperty(), $event->getModel())); + $widget = (new static($event->getEnvironment()))->buildWidget($event->getProperty(), $event->getModel()); + assert($widget instanceof Widget); + + $event->setWidget($widget); } /** @@ -131,7 +139,7 @@ public function getEnvironment() * * @param PropertyInterface $property The property to get the widget class name for. * - * @return string + * @return class-string * * @SuppressWarnings(PHPMD.Superglobals) * @SuppressWarnings(PHPMD.CamelCaseVariableName) @@ -142,13 +150,11 @@ protected function getWidgetClass(PropertyInterface $property) return static::$widgetMapping[$property->getWidgetType()]; } - if (!isset($GLOBALS['BE_FFL'][$property->getWidgetType()])) { - return null; - } - - $className = $GLOBALS['BE_FFL'][$property->getWidgetType()]; + $className = $GLOBALS['BE_FFL'][$property->getWidgetType()] ?? ''; if (!\class_exists($className)) { - return null; + throw new DcGeneralRuntimeException( + \sprintf('Failed to get widget class for property "%s".', $property->getName()) + ); } return $className; @@ -160,7 +166,7 @@ protected function getWidgetClass(PropertyInterface $property) * @param PropertyInterface $propInfo The property for which the X label shall be generated. * @param ModelInterface $model The model. * - * @return array + * @return array|null */ protected function getOptionsForWidget($propInfo, $model): ?array { @@ -173,7 +179,11 @@ protected function getOptionsForWidget($propInfo, $model): ?array $event = new GetPropertyOptionsEvent($environment, $model); $event->setPropertyName($propInfo->getName()); $event->setOptions($options); - $environment->getEventDispatcher()->dispatch($event, GetPropertyOptionsEvent::NAME); + + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $dispatcher->dispatch($event, GetPropertyOptionsEvent::NAME); if ($event->getOptions() !== $options) { return $event->getOptions(); @@ -193,10 +203,9 @@ private function isGetOptionsAllowed(PropertyInterface $property): bool { $propExtra = $property->getExtra(); - // Check the overwrite param. + // Check to overwrite param. if ( - \is_array($propExtra) - && \array_key_exists('fetchOptions', $propExtra) + \array_key_exists('fetchOptions', $propExtra) && (true === $propExtra['fetchOptions']) ) { return true; @@ -219,10 +228,19 @@ private function isGetOptionsAllowed(PropertyInterface $property): bool protected function getTableWizard() { $environment = $this->getEnvironment(); + $dispatcher = $environment->getEventDispatcher(); - $defName = $environment->getDataDefinition()->getName(); - $translator = $environment->getTranslator(); - $urlEvent = new AddToUrlEvent('key=table'); + assert($dispatcher instanceof EventDispatcherInterface); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $defName = $definition->getName(); + + $translator = $environment->getTranslator(); + assert($translator instanceof TranslatorInterface); + + $urlEvent = new AddToUrlEvent('key=table'); $importTableEvent = new GenerateHtmlEvent( 'tablewizard.svg', @@ -261,9 +279,9 @@ protected function getTableWizard() ' %s %s%s', StringUtil::ampersand($urlEvent->getUrl()), StringUtil::specialchars($translator->translate('importTable.1', $defName)), - $importTableEvent->getHtml(), - $shrinkEvent->getHtml(), - $expandEvent->getHtml() + $importTableEvent->getHtml() ?? '', + $shrinkEvent->getHtml() ?? '', + $expandEvent->getHtml() ?? '' ); } @@ -275,9 +293,17 @@ protected function getTableWizard() protected function getListWizard() { $environment = $this->getEnvironment(); - $dispatcher = $environment->getEventDispatcher(); - $defName = $environment->getDataDefinition()->getName(); - $translator = $environment->getTranslator(); + + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $defName = $definition->getName(); + + $translator = $environment->getTranslator(); + assert($translator instanceof TranslatorInterface); $urlEvent = new AddToUrlEvent('key=list'); @@ -294,7 +320,7 @@ protected function getListWizard() ' %s', StringUtil::ampersand($urlEvent->getUrl()), StringUtil::specialchars($translator->translate('importList.1', $defName)), - $importListEvent->getHtml() + $importListEvent->getHtml() ?? '' ); } @@ -310,6 +336,7 @@ protected function getXLabel($propInfo) $xLabel = ''; $environment = $this->getEnvironment(); $translator = $environment->getTranslator(); + assert($translator instanceof TranslatorInterface); // Toggle line wrap (textarea). if (('textarea' === $propInfo->getWidgetType()) && !\array_key_exists('rte', $propInfo->getExtra())) { @@ -323,9 +350,12 @@ protected function getXLabel($propInfo) ) ); - $environment->getEventDispatcher()->dispatch($event, ContaoEvents::IMAGE_GET_HTML); + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $dispatcher->dispatch($event, ContaoEvents::IMAGE_GET_HTML); - $xLabel .= ' ' . $event->getHtml(); + $xLabel .= ' ' . ($event->getHtml() ?? ''); } $xLabel .= $this->getHelpWizard($propInfo); @@ -355,6 +385,8 @@ protected function getHelpWizard($propInfo) $helpWizard = ''; $environment = $this->getEnvironment(); $translator = $environment->getTranslator(); + assert($translator instanceof TranslatorInterface); + // Add the help wizard. if ($propInfo->getExtra() && \array_key_exists('helpwizard', $propInfo->getExtra())) { $event = new GenerateHtmlEvent( @@ -363,16 +395,22 @@ protected function getHelpWizard($propInfo) 'style="vertical-align:text-bottom;"' ); - $environment->getEventDispatcher()->dispatch($event, ContaoEvents::IMAGE_GET_HTML); + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $dispatcher->dispatch($event, ContaoEvents::IMAGE_GET_HTML); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); $helpWizard .= \sprintf( ' %s', - $environment->getDataDefinition()->getName(), + $definition->getName(), $propInfo->getName(), StringUtil::specialchars($translator->translate('MSC.helpWizard')), - $event->getHtml() + $event->getHtml() ?? '' ); } @@ -385,7 +423,7 @@ protected function getHelpWizard($propInfo) * @param PropertyInterface $property The property. * @param ModelInterface $model The current model. * - * @return Widget + * @return Widget|null * * @throws DcGeneralRuntimeException When not running in TL_MODE BE. * @@ -413,14 +451,18 @@ public function buildWidget( $prepareAttributes = $this->prepareWidgetAttributes($model, $property); $widget = new $class($prepareAttributes, new DcCompat($environment, $model, $property->getName())); + assert($widget instanceof Widget); // OH: what is this? source: DataContainer 232. $widget->currentRecord = $model->getId(); $widget->xlabel .= $this->getXLabel($property); + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + $event = new ManipulateWidgetEvent($environment, $model, $property, $widget); - $environment->getEventDispatcher()->dispatch($event, ManipulateWidgetEvent::NAME); + $dispatcher->dispatch($event, ManipulateWidgetEvent::NAME); return $widget; } @@ -442,7 +484,10 @@ private function valueToWidget(ModelInterface $model, PropertyInterface $propert ->setProperty($property->getName()) ->setValue($model->getProperty($property->getName())); - $environment->getEventDispatcher()->dispatch($event, $event::NAME); + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $dispatcher->dispatch($event, $event::NAME); $value = $event->getValue(); $propExtra = $property->getExtra(); @@ -472,11 +517,13 @@ private function valueToWidget(ModelInterface $model, PropertyInterface $propert private function prepareWidgetAttributes(ModelInterface $model, PropertyInterface $property) { $environment = $this->getEnvironment(); - $defName = $environment->getDataDefinition()->getName(); - $propExtra = $property->getExtra(); + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); - $value = $this->valueToWidget($model, $property); + $defName = $definition->getName(); + $propExtra = $property->getExtra(); + $value = $this->valueToWidget($model, $property); $propExtra['required'] = ('' === $value) && !empty($propExtra['mandatory']); @@ -505,7 +552,10 @@ private function prepareWidgetAttributes(ModelInterface $model, PropertyInterfac new DcCompat($environment, $model, $property->getName()) ); - $environment->getEventDispatcher()->dispatch($event, ContaoEvents::WIDGET_GET_ATTRIBUTES_FROM_DCA); + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $dispatcher->dispatch($event, ContaoEvents::WIDGET_GET_ATTRIBUTES_FROM_DCA); $prepareAttributes = $event->getResult(); if ( diff --git a/src/Contao/View/Contao2BackendView/TreePicker.php b/src/Contao/View/Contao2BackendView/TreePicker.php index d01ad303..ff8fd52e 100644 --- a/src/Contao/View/Contao2BackendView/TreePicker.php +++ b/src/Contao/View/Contao2BackendView/TreePicker.php @@ -28,6 +28,7 @@ use Contao\Backend; use Contao\CoreBundle\Exception\ResponseException; +use Contao\CoreBundle\Picker\PickerBuilderInterface; use Contao\CoreBundle\Picker\PickerConfig; use Contao\StringUtil; use Contao\System; @@ -36,7 +37,11 @@ use ContaoCommunityAlliance\Contao\Bindings\Events\Backend\AddToUrlEvent; use ContaoCommunityAlliance\Contao\Bindings\Events\Controller\ReloadEvent; use ContaoCommunityAlliance\Contao\Bindings\Events\Image\GenerateHtmlEvent; +use ContaoCommunityAlliance\DcGeneral\BaseConfigRegistryInterface; +use ContaoCommunityAlliance\DcGeneral\Contao\Compatibility\DcCompat; use ContaoCommunityAlliance\DcGeneral\Contao\DataDefinition\Definition\Contao2BackendViewDefinitionInterface; +use ContaoCommunityAlliance\DcGeneral\Contao\Factory\SessionStorageFactory; +use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminator; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\ModelToLabelEvent; use ContaoCommunityAlliance\DcGeneral\Controller\ModelCollector; use ContaoCommunityAlliance\DcGeneral\Controller\RelationshipManager; @@ -47,27 +52,40 @@ use ContaoCommunityAlliance\DcGeneral\Data\DCGE; use ContaoCommunityAlliance\DcGeneral\Data\ModelId; use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\BasicDefinitionInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\DefaultModelFormatterConfig; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\ListingConfigInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\ModelFormatterConfigInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\ModelRelationship\FilterBuilder; use ContaoCommunityAlliance\DcGeneral\DC\General; +use ContaoCommunityAlliance\DcGeneral\DcGeneral; use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralRuntimeException; use ContaoCommunityAlliance\DcGeneral\Factory\DcGeneralFactory; +use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; +use ContaoCommunityAlliance\DcGeneral\SessionStorageInterface; +use ContaoCommunityAlliance\Translator\TranslatorInterface; +use Symfony\Cmf\Component\Routing\ChainRouterInterface; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Generator\UrlGenerator; /** * Provide methods to handle input field "tableTree". * + * @property array{fields: list, format: string, maxCharacters: int}|null $itemLabel + * @property string $searchField + * * @SuppressWarnings(PHPMD.TooManyPublicMethods) * @SuppressWarnings(PHPMD.TooManyFields) * @SuppressWarnings(PHPMD.TooManyMethods) * @SuppressWarnings(PHPMD.ExcessiveClassLength) * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * @psalm-suppress PropertyNotSetInConstructor */ class TreePicker extends Widget { @@ -92,27 +110,6 @@ class TreePicker extends Widget */ protected $subTemplate = 'widget_treepicker'; - /** - * The ajax id. - * - * @var string - */ - protected $strAjaxId; - - /** - * The ajax key. - * - * @var string - */ - protected $strAjaxKey; - - /** - * The ajax name. - * - * @var string - */ - protected $strAjaxName; - /** * The data Container. * @@ -125,7 +122,7 @@ class TreePicker extends Widget * * @var string */ - protected $sourceName; + protected $sourceName = ''; /** * The field type to use. @@ -136,13 +133,6 @@ class TreePicker extends Widget */ protected $fieldType = 'radio'; - /** - * Flag determining if the value shall always be saved. - * - * @var bool - */ - protected $alwaysSave; - /** * The minimum level for items to be selectable. * @@ -169,12 +159,12 @@ class TreePicker extends Widget * * @var string */ - protected $title; + protected $title = ''; /** * The data container for the item source. * - * @var General + * @var DcGeneral */ protected $itemContainer; @@ -183,28 +173,28 @@ class TreePicker extends Widget * * @var string */ - protected $orderField; + protected $orderField = ''; /** * The tree nodes to be handled. * - * @var TreeNodeStates + * @var TreeNodeStates|null */ - protected $nodeStates; + protected $nodeStates = null; /** * Create a new instance. * - * @param array $attributes The custom attributes. - * @param General $dataContainer The data container. + * @param array $attributes The custom attributes. + * @param General|null $dataContainer The data container. * - * @throws DcGeneralRuntimeException When not in Backend mode. */ public function __construct($attributes = [], General $dataContainer = null) { parent::__construct($attributes); $scopeDeterminator = System::getContainer()->get('cca.dc-general.scope-matcher'); + assert($scopeDeterminator instanceof RequestScopeDeterminator); if ($scopeDeterminator->currentScopeIsUnknown() || !$scopeDeterminator->currentScopeIsBackend()) { throw new DcGeneralRuntimeException('Treepicker is currently for Backend only.'); @@ -216,42 +206,59 @@ public function __construct($attributes = [], General $dataContainer = null) /** * Setup all local values and create the dc instance for the referenced data source. * - * @param General $dataContainer The data container to use. + * @param General|null $dataContainer The data container to use. * * @return void */ protected function setUp(General $dataContainer = null) { - $this->dataContainer = $dataContainer ?: $this->objDca; + if (null === $dataContainer) { + /** @var General|null $dataContainer */ + $dataContainer = $this->objDca; + } - if (!$this->dataContainer) { + if (!$dataContainer) { return; } + $this->dataContainer = $dataContainer; + $environment = $this->dataContainer->getEnvironment(); if (!$this->sourceName) { - $property = $environment - ->getDataDefinition() + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + + $property = $definition ->getPropertiesDefinition() - ->getProperty($environment->getInputProvider()->getValue('name')); + ->getProperty($inputProvider->getValue('name')); foreach ($property->getExtra() as $k => $v) { $this->$k = $v; } - $name = $environment->getInputProvider()->getValue('name'); + $name = $inputProvider->getValue('name'); $this->strField = $name; $this->strName = $name; $this->strId = $name; $this->label = $property->getLabel() ?: $name; - $this->strTable = $environment->getDataDefinition()->getName(); + $this->strTable = $definition->getName(); } + $translator = $environment->getTranslator(); + assert($translator instanceof TranslatorInterface); + + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + /** @psalm-suppress PropertyNotSetInConstructor */ $this->itemContainer = (new DcGeneralFactory()) ->setContainerName($this->sourceName) - ->setTranslator($environment->getTranslator()) - ->setEventDispatcher($environment->getEventDispatcher()) + ->setTranslator($translator) + ->setEventDispatcher($dispatcher) ->createDcGeneral(); } @@ -279,10 +286,16 @@ public function updateAjax($ajaxAction, $dataContainer) $this->setUp($dataContainer); $environment = $this->dataContainer->getEnvironment(); - $this->value = $environment->getInputProvider()->getValue('value'); - $label = $environment - ->getDataDefinition() + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + + $this->value = $inputProvider->getValue('value'); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $label = $definition ->getPropertiesDefinition() ->getProperty($this->strName) ->getDescription(); @@ -292,7 +305,7 @@ public function updateAjax($ajaxAction, $dataContainer) $result = '' . '

' . $this->generate(); - if (null !== $label && $GLOBALS['TL_CONFIG']['showHelp']) { + if ($GLOBALS['TL_CONFIG']['showHelp']) { $result .= '

' . $label . '

'; } @@ -302,7 +315,7 @@ public function updateAjax($ajaxAction, $dataContainer) /** * Retrieve the item container. * - * @return General + * @return DcGeneral */ public function getItemContainer() { @@ -327,25 +340,35 @@ public function getEnvironment() public function getTreeNodeStates() { if (!isset($this->nodeStates)) { - $environment = $this->getEnvironment(); - $sessionStorage = $environment->getSessionStorage(); + $environment = $this->getEnvironment(); + + $sessionStorage = $environment->getSessionStorage(); + assert($sessionStorage instanceof SessionStorageInterface); + $this->nodeStates = new TreeNodeStates( $sessionStorage->get($this->getToggleId()), $this->determineParentsOfValues() ); + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + // Maybe it is not the best location to do this here. - if ('all' === $environment->getInputProvider()->getParameter('ptg')) { + if ('all' === $inputProvider->getParameter('ptg')) { // Save in session and reload. $sessionStorage->set( $this->getToggleId(), $this->nodeStates->setAllOpen($this->nodeStates->isAllOpen())->getStates() ); - $environment->getEventDispatcher()->dispatch(new ReloadEvent(), ContaoEvents::CONTROLLER_RELOAD); + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $dispatcher->dispatch(new ReloadEvent(), ContaoEvents::CONTROLLER_RELOAD); } } + /** @psalm-suppress PropertyNotSetInConstructor */ return $this->nodeStates; } @@ -397,9 +420,9 @@ public function __set($key, $value) /** * Convert the value according to the configured fieldtype. * - * @param mixed $value The value to convert. + * @param null|list|string $value The value to convert. * - * @return mixed + * @return null|string|list * * @throws \RuntimeException When an unknown field type is encountered. */ @@ -418,8 +441,10 @@ private function convertValue($value) return $value; case 'checkbox': $delimiter = (false !== \stripos($value, "\t")) ? "\t" : ','; + /** @var array $files */ + $files = StringUtil::trimsplit($delimiter, $value); - return StringUtil::trimsplit($delimiter, $value); + return \array_values($files); default: } throw new \RuntimeException('Unknown field type encountered: ' . $this->fieldType); @@ -434,10 +459,13 @@ private function convertValue($value) * * fieldType : the input type. Either "radio" or "checkbox". * * titleIcon: the icon to use in the title section. * * mandatory: the field value must not be empty. + * * orderField: the field to order items. * * @param string $key The property name. * - * @return string The property value. + * @return mixed The property value. + * + * @psalm-suppress LessSpecificImplementedReturnType */ public function __get($key) { @@ -452,7 +480,7 @@ public function __get($key) return $this->titleIcon; case 'mandatory': - return $this->arrConfiguration['mandatory'] ?? false; + return $this->arrConfiguration['mandatory'] ?? ''; case 'orderField': return $this->orderField; @@ -462,27 +490,32 @@ public function __get($key) default: } + return parent::__get($key); } /** * Skip the field if "change selection" is not checked. * - * @param array $value The current value. + * @param null|string|list $varInput The current value. + * + * @return null|string|list * - * @return array|string + * @psalm-suppress MoreSpecificImplementedParamType */ - protected function validator($value) + protected function validator($varInput) { - $convertValue = $this->widgetToValue($value); + $convertValue = $this->widgetToValue($varInput); if ((null === $convertValue) && $this->mandatory) { $translator = $this->getEnvironment()->getTranslator(); + assert($translator instanceof TranslatorInterface); $message = empty($this->label) ? $translator->translate('ERR.mdtryNoLabel') : \sprintf( $translator->translate('ERR.mandatory'), + /** @psalm-suppress PropertyNotSetInConstructor */ $this->strLabel ); @@ -501,8 +534,9 @@ protected function validator($value) */ public function renderItemsPlain() { - $values = []; - $value = $this->varValue; + $values = []; + $value = $this->varValue; + /** @psalm-suppress UndefinedThisPropertyFetch */ $idProperty = $this->idProperty ?: 'id'; if ('radio' === $this->fieldType && !empty($value)) { @@ -511,8 +545,14 @@ public function renderItemsPlain() if (\is_array($value) && !empty($value)) { $environment = $this->getEnvironment(); - $dataDriver = $environment->getDataProvider(); - $config = $environment->getBaseConfigRegistry()->getBaseConfig(); + + $dataDriver = $environment->getDataProvider(); + assert($dataDriver instanceof DataProviderInterface); + + $registry = $environment->getBaseConfigRegistry(); + assert($registry instanceof BaseConfigRegistryInterface); + + $config = $registry->getBaseConfig(); $filter = FilterBuilder::fromArrayForRoot() ->getFilter() ->andPropertyValueIn($idProperty, $value) @@ -526,6 +566,10 @@ public function renderItemsPlain() } foreach ($dataDriver->fetchAll($config) as $model) { + if (!($model instanceof ModelInterface)) { + continue; + } + $formatted = $this->formatModel($model, false); $idValue = $model->getProperty($idProperty); $values[$idValue] = $formatted[0]['content']; @@ -580,16 +624,25 @@ public function generate() $GLOBALS['TL_JAVASCRIPT']['cca.dc-general.vanillaGeneral'] = 'bundles/ccadcgeneral/js/vanillaGeneral.js'; $environment = $this->getEnvironment(); - $translator = $environment->getTranslator(); + + $translator = $environment->getTranslator(); + assert($translator instanceof TranslatorInterface); + $template = new ContaoBackendViewTemplate('widget_treepicker'); + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + $icon = new GenerateHtmlEvent($this->titleIcon); - $environment->getEventDispatcher()->dispatch($icon, ContaoEvents::IMAGE_GET_HTML); + $dispatcher->dispatch($icon, ContaoEvents::IMAGE_GET_HTML); $template ->setTranslator($translator) + /** @psalm-suppress PropertyNotSetInConstructor */ ->set('id', $this->strId) + /** @psalm-suppress PropertyNotSetInConstructor */ ->set('name', $this->strName) + /** @psalm-suppress PropertyNotSetInConstructor */ ->set('class', ($this->strClass ? ' ' . $this->strClass : '')) ->set('icon', $icon->getHtml()) ->set('title', $translator->translate($this->title ?: 'MSC.treePicker', null, [$this->sourceName])) @@ -618,14 +671,18 @@ public function generate() */ protected function generatePickerUrl() { + assert($this->dataContainer instanceof DcCompat); + $model = $this->dataContainer->getModel(); + assert($model instanceof ModelInterface); + $parameter = [ 'fieldType' => $this->fieldType, 'sourceName' => $this->sourceName, - 'modelId' => ModelId::fromModel($this->dataContainer->getModel())->getSerialized(), + 'modelId' => ModelId::fromModel($model)->getSerialized(), 'orderField' => $this->orderField, 'propertyName' => $this->name ]; - + /** @psalm-suppress UndefinedThisPropertyFetch */ if ($this->pickerOrderProperty && $this->pickerSortDirection) { $parameter = \array_merge( $parameter, @@ -636,15 +693,18 @@ protected function generatePickerUrl() ); } - return System::getContainer()->get('contao.picker.builder')->getUrl('cca_tree', $parameter); + $pickerBuilder = System::getContainer()->get('contao.picker.builder'); + assert($pickerBuilder instanceof PickerBuilderInterface); + + return $pickerBuilder->getUrl('cca_tree', $parameter); } /** * Convert the value from widget for internal process. * - * @param mixed $value The widget value. + * @param null|string|list $value The widget value. * - * @return null|string|array + * @return null|string|list */ public function widgetToValue($value) { @@ -680,21 +740,31 @@ public function valueToWidget($value) */ protected function generateUpdateUrl() { - $request = System::getContainer()->get('request_stack')->getCurrentRequest(); + $requestStack = System::getContainer()->get('request_stack'); + assert($requestStack instanceof RequestStack); + + $request = $requestStack->getCurrentRequest(); + assert($request instanceof Request); + + $dataContainer = $this->dataContainer; + assert($dataContainer instanceof General); $configPicker = new PickerConfig( 'cca_tree', [ 'fieldType' => $this->fieldType, 'sourceName' => $this->sourceName, - 'modelId' => ModelId::fromModel($this->dataContainer->getModel())->getSerialized(), + 'modelId' => ModelId::fromModel($dataContainer->getModel())->getSerialized(), 'orderField' => $this->orderField, 'propertyName' => $this->name ], $this->valueToWidget($this->value) ); - return System::getContainer()->get('router')->generate( + $router = System::getContainer()->get('router'); + assert($router instanceof ChainRouterInterface); + + return $router->generate( 'cca_dc_general_tree_update', [ 'picker' => $configPicker->cloneForCurrent((string) $request->query->get('context'))->urlEncode() @@ -717,9 +787,16 @@ private function generateBreadCrumbUrl(ModelInterface $model) $toggleUrlEvent = new AddToUrlEvent( 'ptg=' . $model->getId() . '&provider=' . $model->getProviderName() ); - $this->getEnvironment()->getEventDispatcher()->dispatch($toggleUrlEvent, ContaoEvents::BACKEND_ADD_TO_URL); - return System::getContainer()->get('router')->generate( + $dispatcher = $this->getEnvironment()->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $dispatcher->dispatch($toggleUrlEvent, ContaoEvents::BACKEND_ADD_TO_URL); + + $router = System::getContainer()->get('router'); + assert($router instanceof ChainRouterInterface); + + return $router->generate( 'cca_dc_general_tree_breadcrumb', $this->getQueryParameterFromUrl($toggleUrlEvent->getUrl()), UrlGenerator::ABSOLUTE_URL @@ -738,9 +815,16 @@ private function generateToggleUrl(ModelInterface $model) $toggleUrlEvent = new AddToUrlEvent( 'ptg=' . $model->getId() . '&provider=' . $model->getProviderName() ); - $this->getEnvironment()->getEventDispatcher()->dispatch($toggleUrlEvent, ContaoEvents::BACKEND_ADD_TO_URL); - return System::getContainer()->get('router')->generate( + $dispatcher = $this->getEnvironment()->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $dispatcher->dispatch($toggleUrlEvent, ContaoEvents::BACKEND_ADD_TO_URL); + + $router = System::getContainer()->get('router'); + assert($router instanceof ChainRouterInterface); + + return $router->generate( 'cca_dc_general_tree_toggle', $this->getQueryParameterFromUrl($toggleUrlEvent->getUrl()), UrlGenerator::ABSOLUTE_URL @@ -756,7 +840,7 @@ private function generateToggleUrl(ModelInterface $model) */ private function getQueryParameterFromUrl($url) { - $parameters = array(); + $parameters = []; foreach (\preg_split('/&(amp;)?/i', \preg_split('/[?]/ui', $url)[1]) as $value) { $chunks = \explode('=', $value); $parameters[$chunks[0]] = $chunks[1]; @@ -779,7 +863,9 @@ private function addOrderFieldToTemplate(ContaoBackendViewTemplate $template) } $translator = $this->getEnvironment()->getTranslator(); + assert($translator instanceof TranslatorInterface); + /** @psalm-suppress UndefinedThisPropertyFetch */ $template ->set('hasOrder', true) ->set('orderId', $this->orderField) @@ -802,16 +888,25 @@ public function generatePopup() $GLOBALS['TL_JAVASCRIPT']['cca.dc-general.vanillaGeneral'] = 'bundles/ccadcgeneral/js/vanillaGeneral.js'; $environment = $this->getEnvironment(); - $translator = $environment->getTranslator(); - $template = new ContaoBackendViewTemplate('widget_treepicker_popup'); + + $translator = $environment->getTranslator(); + assert($translator instanceof TranslatorInterface); + + $template = new ContaoBackendViewTemplate('widget_treepicker_popup'); $icon = new GenerateHtmlEvent($this->titleIcon); - $environment->getEventDispatcher()->dispatch($icon, ContaoEvents::IMAGE_GET_HTML); + + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $dispatcher->dispatch($icon, ContaoEvents::IMAGE_GET_HTML); $template ->setTranslator($translator) ->set('id', 'tl_listing') + /** @psalm-suppress PropertyNotSetInConstructor */ ->set('name', $this->strName) + /** @psalm-suppress PropertyNotSetInConstructor */ ->set('class', ($this->strClass ? ' ' . $this->strClass : '')) ->set('icon', $icon->getHtml()) ->set('title', $translator->translate($this->title ?: 'MSC.treePicker', null, [$this->sourceName])) @@ -841,6 +936,7 @@ public function generatePopup() */ private function getRootIds() { + /** @psalm-suppress UndefinedThisPropertyFetch */ $root = $this->root; $root = \is_array($root) ? $root : ((\is_numeric($root) && $root > 0) ? [$root] : []); $root = \array_merge($root, [0]); @@ -856,15 +952,22 @@ private function getRootIds() public function generateAjax() { $input = $this->getEnvironment()->getInputProvider(); + assert($input instanceof InputProviderInterface); if ($input->hasValue('action') && ('DcGeneralLoadSubTree' === $input->getValue('action'))) { $provider = $input->getValue('providerName'); $rootId = $input->getValue('id'); - $this->getEnvironment()->getSessionStorage()->set( + + $sessionStorage = $this->getEnvironment()->getSessionStorage(); + assert($sessionStorage instanceof SessionStorageInterface); + + $sessionStorage->set( $this->getToggleId(), $this->getTreeNodeStates()->toggleModel($provider, $rootId)->getStates() ); + $collection = $this->loadCollection($rootId, ((int) $input->getValue('level') + 1)); + return $this->generateTreeView($collection, 'tree'); } @@ -878,7 +981,10 @@ public function generateAjax() */ protected function getToggleId() { - return $this->getEnvironment()->getDataDefinition()->getName() . $this->strId . '_tree'; + $definition = $this->getEnvironment()->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + return $definition->getName() . $this->strId . '_tree'; } /** @@ -888,7 +994,10 @@ protected function getToggleId() */ public function getSearchSessionKey() { - return $this->getEnvironment()->getDataDefinition()->getName() . $this->strId . '_tree_search'; + $definition = $this->getEnvironment()->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + return $definition->getName() . $this->strId . '_tree_search'; } /** @@ -932,8 +1041,12 @@ protected function determineModelState(ModelInterface $model, $level) */ protected function treeWalkModel(ModelInterface $model, $level, $subTables = []) { - $environment = $this->getEnvironment(); - $relationships = $environment->getDataDefinition()->getModelRelationshipDefinition(); + $environment = $this->getEnvironment(); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $relationships = $definition->getModelRelationshipDefinition(); $hasChild = false; $this->determineModelState($model, ($level - 1)); @@ -949,12 +1062,15 @@ protected function treeWalkModel(ModelInterface $model, $level, $subTables = []) } // Create a new Config and fetch the children from the child provider. - $childConfig = $environment->getDataProvider($subTable)->getEmptyConfig(); + $dataProvider = $environment->getDataProvider($subTable); + assert($dataProvider instanceof DataProviderInterface); + + $childConfig = $dataProvider->getEmptyConfig(); $childConfig->setFilter($childFilter->getFilter($model)); $childConfig->setSorting(['sorting' => 'ASC']); - - $childCollection = $environment->getDataProvider($subTable)->fetchAll($childConfig); + $childCollection = $dataProvider->fetchAll($childConfig); + assert($childCollection instanceof CollectionInterface); $hasChild = ($childCollection->length() > 0); @@ -994,10 +1110,13 @@ protected function treeWalkModel(ModelInterface $model, $level, $subTables = []) */ private function treeWalkChildCollection(CollectionInterface $childCollection, ModelInterface $model, $level) { - $relationships = $this->getEnvironment()->getDataDefinition()->getModelRelationshipDefinition(); + $definition = $this->getEnvironment()->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $relationships = $definition->getModelRelationshipDefinition(); foreach ($childCollection as $childModel) { - // Let the child know about it's parent. + // Let the child know about its parent. $model->setMeta($model::PARENT_ID, $model->getId()); $model->setMeta($model::PARENT_PROVIDER_NAME, $model->getProviderName()); @@ -1013,21 +1132,32 @@ private function treeWalkChildCollection(CollectionInterface $childCollection, M /** * Recursively retrieve a collection of all complete node hierarchy. * - * @param array $rootId The ids of the root node. - * @param int $level The level the items are residing on. - * @param string $providerName The data provider from which the root element originates from. + * @param mixed|null $rootId The ids of the root node. + * @param int $level The level the items are residing on. + * @param string $providerName The data provider from which the root element originates from. * * @return CollectionInterface */ public function getTreeCollectionRecursive($rootId, $level = 0, $providerName = null) { - $environment = $this->getEnvironment(); - $dataDriver = $environment->getDataProvider($providerName); + $environment = $this->getEnvironment(); + + $dataDriver = $environment->getDataProvider($providerName); + assert($dataDriver instanceof DataProviderInterface); + $tableTreeData = $dataDriver->getEmptyCollection(); - $rootConfig = $environment->getBaseConfigRegistry()->getBaseConfig(); - $relationships = $environment->getDataDefinition()->getModelRelationshipDefinition(); - if (!$rootId) { + $registry = $environment->getBaseConfigRegistry(); + assert($registry instanceof BaseConfigRegistryInterface); + + $rootConfig = $registry->getBaseConfig(); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $relationships = $definition->getModelRelationshipDefinition(); + + if (null === $rootId) { $this->prepareFilterForRootCondition(); $this->pushRootModelToTreeCollection($dataDriver, $tableTreeData, $level); @@ -1037,6 +1167,7 @@ public function getTreeCollectionRecursive($rootId, $level = 0, $providerName = $rootConfig->setId($rootId); // Fetch root element. $rootModel = $dataDriver->fetch($rootConfig); + assert($rootModel instanceof ModelInterface); $mySubTables = []; foreach ($relationships->getChildConditions($rootModel->getProviderName()) as $condition) { @@ -1057,10 +1188,17 @@ public function getTreeCollectionRecursive($rootId, $level = 0, $providerName = */ private function prepareFilterForRootCondition() { - $environment = $this->getEnvironment(); - $rootCondition = $environment->getDataDefinition()->getModelRelationshipDefinition()->getRootCondition(); + $environment = $this->getEnvironment(); - $baseConfig = $environment->getBaseConfigRegistry()->getBaseConfig(); + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $rootCondition = $definition->getModelRelationshipDefinition()->getRootCondition(); + + $registry = $environment->getBaseConfigRegistry(); + assert($registry instanceof BaseConfigRegistryInterface); + + $baseConfig = $registry->getBaseConfig(); if (!$rootCondition) { return $baseConfig; } @@ -1089,12 +1227,19 @@ private function prepareFilterForRootCondition() private function pushRootModelToTreeCollection( DataProviderInterface $dataProvider, CollectionInterface $treeCollection, - $level + int $level ) { - $environment = $this->getEnvironment(); + $environment = $this->getEnvironment(); + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + $baseConfig = $this->prepareFilterForRootCondition(); - $relationships = $environment->getDataDefinition()->getModelRelationshipDefinition(); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $relationships = $definition->getModelRelationshipDefinition(); if ($inputProvider->hasParameter('orderProperty') && $inputProvider->hasParameter('sortDirection')) { $orderProperty = $inputProvider->getParameter('orderProperty'); @@ -1105,12 +1250,17 @@ private function pushRootModelToTreeCollection( // Fetch all root elements. $collection = $dataProvider->fetchAll($baseConfig); + assert($collection instanceof CollectionInterface); + if (!$collection->count()) { return; } + $firstModel = $collection->get(0); + assert($firstModel instanceof ModelInterface); + $mySubTables = []; - foreach ($relationships->getChildConditions($collection->get(0)->getProviderName()) as $condition) { + foreach ($relationships->getChildConditions($firstModel->getProviderName()) as $condition) { $mySubTables[] = $condition->getDestinationName(); } @@ -1124,9 +1274,9 @@ private function pushRootModelToTreeCollection( /** * Load the collection of child items and the parent item for the currently selected parent item. * - * @param mixed $rootId The root element (or null to fetch everything). - * @param int $level The current level in the tree (of the optional root element). - * @param null $providerName The data provider from which the optional root element shall be taken from. + * @param mixed|null $rootId The root element (or null to fetch everything). + * @param int $level The current level in the tree (of the optional root element). + * @param null $providerName The data provider from which the optional root element shall be taken from. * * @return CollectionInterface */ @@ -1136,9 +1286,14 @@ public function loadCollection($rootId = null, $level = 0, $providerName = null) $collection = $this->getTreeCollectionRecursive($rootId, $level, $providerName); - if ($rootId) { - $treeData = $environment->getDataProvider($providerName)->getEmptyCollection(); + if (null !== $rootId) { + $dataProvider = $environment->getDataProvider($providerName); + assert($dataProvider instanceof DataProviderInterface); + + $treeData = $dataProvider->getEmptyCollection(); $model = $collection->get(0); + assert($model instanceof ModelInterface); + foreach ($model->getMeta($model::CHILD_COLLECTIONS) as $collection) { foreach ($collection as $subModel) { $treeData->push($subModel); @@ -1162,7 +1317,12 @@ protected function getFormatter(ModelInterface $model, $treeMode) { /** @var ListingConfigInterface $listing */ $definition = $this->getEnvironment()->getDataDefinition(); - $listing = $definition->getDefinition(Contao2BackendViewDefinitionInterface::NAME)->getListingConfig(); + assert($definition instanceof ContainerInterface); + + $backendView = $definition->getDefinition(Contao2BackendViewDefinitionInterface::NAME); + assert($backendView instanceof Contao2BackendViewDefinitionInterface); + + $listing = $backendView->getListingConfig(); if ($listing->hasLabelFormatter($model->getProviderName())) { return $listing->getLabelFormatter($model->getProviderName()); @@ -1170,7 +1330,7 @@ protected function getFormatter(ModelInterface $model, $treeMode) // If not in tree mode and custom label has been defined, use it. if (!$treeMode && $this->itemLabel) { - $label = (array) $this->itemLabel; + $label = $this->itemLabel; $formatter = new DefaultModelFormatterConfig(); $formatter->setPropertyNames($label['fields']); $formatter->setFormat($label['format']); @@ -1204,13 +1364,18 @@ protected function getFormatter(ModelInterface $model, $treeMode) public function formatModel(ModelInterface $model, $treeMode = true) { /** @var ListingConfigInterface $listing */ - $environment = $this->getEnvironment(); - $definition = $environment->getDataDefinition(); - $listing = $definition - ->getDefinition(Contao2BackendViewDefinitionInterface::NAME) - ->getListingConfig(); - $properties = $definition->getPropertiesDefinition(); - $defaultSortFields = \array_keys((array) $listing->getDefaultSortingFields()); + $environment = $this->getEnvironment(); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $backendView = $definition->getDefinition(Contao2BackendViewDefinitionInterface::NAME); + assert($backendView instanceof Contao2BackendViewDefinitionInterface); + + $listing = $backendView->getListingConfig(); + $properties = $definition->getPropertiesDefinition(); + /** @psalm-suppress DeprecatedMethod */ + $defaultSortFields = \array_keys($listing->getDefaultSortingFields()); $firstSorting = \reset($defaultSortFields); $formatter = $this->getFormatter($model, $treeMode); @@ -1229,7 +1394,10 @@ public function formatModel(ModelInterface $model, $treeMode = true) ->setLabel($formatter->getFormat()) ->setFormatter($formatter); - $environment->getEventDispatcher()->dispatch($event, $event::NAME); + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $dispatcher->dispatch($event, $event::NAME); $labelList = []; $this->prepareLabelWithDisplayedProperties($formatter, $event->getArgs(), $firstSorting, $labelList); @@ -1243,7 +1411,7 @@ public function formatModel(ModelInterface $model, $treeMode = true) * * @param ModelFormatterConfigInterface $formatter The model formatter. * @param array $arguments The model label arguments. - * @param string|bool $firstSorting The first sorting. + * @param bool|string $firstSorting The first sorting. * @param array $labelList The label list. * * @return void @@ -1251,11 +1419,16 @@ public function formatModel(ModelInterface $model, $treeMode = true) private function prepareLabelWithDisplayedProperties( ModelFormatterConfigInterface $formatter, array $arguments, - $firstSorting, + bool|string $firstSorting, array &$labelList ) { $definition = $this->getEnvironment()->getDataDefinition(); - $listing = $definition->getDefinition(Contao2BackendViewDefinitionInterface::NAME)->getListingConfig(); + assert($definition instanceof ContainerInterface); + + $backendView = $definition->getDefinition(Contao2BackendViewDefinitionInterface::NAME); + assert($backendView instanceof Contao2BackendViewDefinitionInterface); + + $listing = $backendView->getListingConfig(); if (!$listing->getShowColumns()) { return; } @@ -1284,18 +1457,23 @@ private function prepareLabelWithDisplayedProperties( private function prepareLabelWithOutDisplayedProperties( ModelFormatterConfigInterface $formatter, array $arguments, - $label, + string $label, array &$labelList ) { $definition = $this->getEnvironment()->getDataDefinition(); - $listing = $definition->getDefinition(Contao2BackendViewDefinitionInterface::NAME)->getListingConfig(); + assert($definition instanceof ContainerInterface); + + $backendView = $definition->getDefinition(Contao2BackendViewDefinitionInterface::NAME); + assert($backendView instanceof Contao2BackendViewDefinitionInterface); + + $listing = $backendView->getListingConfig(); if ($listing->getShowColumns()) { return; } $string = \vsprintf($label, $arguments); - if (($maxLength = null !== $formatter->getMaxLength()) && \strlen($string) > $maxLength) { + if ((null !== $maxLength = $formatter->getMaxLength()) && \strlen($string) > $maxLength) { $string = \substr($string, 0, $maxLength); } @@ -1318,10 +1496,13 @@ protected function parseModel($model, $toggleID) { $model->setMeta($model::LABEL_VALUE, $this->formatModel($model)); + $translator = $this->getEnvironment()->getTranslator(); + assert($translator instanceof TranslatorInterface); + if ($model->getMeta($model::SHOW_CHILDREN)) { - $toggleTitle = $this->getEnvironment()->getTranslator()->translate('MSC.collapseNode'); + $toggleTitle = $translator->translate('MSC.collapseNode'); } else { - $toggleTitle = $this->getEnvironment()->getTranslator()->translate('MSC.expandNode'); + $toggleTitle = $translator->translate('MSC.expandNode'); } $toggleScript = \sprintf( @@ -1335,8 +1516,9 @@ protected function parseModel($model, $toggleID) ); $template = new ContaoBackendViewTemplate('widget_treepicker_entry'); + /** @psalm-suppress UndefinedThisPropertyFetch */ $template - ->setTranslator($this->getEnvironment()->getTranslator()) + ->setTranslator($translator) ->set('id', $this->strId) ->set('name', $this->strName) ->set('theme', Backend::getTheme()) @@ -1387,8 +1569,11 @@ protected function generateTreeView($collection, $treeClass) $subHtml .= $this->generateTreeView($objChildCollection, $treeClass); } + $translator = $this->getEnvironment()->getTranslator(); + assert($translator instanceof TranslatorInterface); + $template - ->setTranslator($this->getEnvironment()->getTranslator()) + ->setTranslator($translator) ->set('objParentModel', $model) ->set('strToggleID', $toggleID) ->set('strHTML', $subHtml) @@ -1406,22 +1591,30 @@ protected function generateTreeView($collection, $treeClass) * Fetch all parents of the passed model. * * @param ModelInterface $model The model. - * @param string[] $parents The ids of all detected parents so far. + * @param array $parents The ids of all detected parents so far. * * @return void */ private function parentsOf($model, &$parents) { - $environment = $this->getEnvironment(); + $environment = $this->getEnvironment(); + $dataDefinition = $environment->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + + $mode = $dataDefinition->getBasicDefinition()->getMode(); + assert(\is_int($mode)); + $collector = new ModelCollector($this->getEnvironment()); $relationships = new RelationshipManager( $dataDefinition->getModelRelationshipDefinition(), - $dataDefinition->getBasicDefinition()->getMode() + $mode ); if (!$relationships->isRoot($model)) { $parent = $collector->searchParentOf($model); + assert($parent instanceof ModelInterface); + if (!isset($parents[$model->getProviderName()][$parent->getId()])) { $this->parentsOf($parent, $parents); } @@ -1439,14 +1632,24 @@ private function determineParentsOfValues() { $parents = []; $environment = $this->getEnvironment(); - $mode = $environment->getDataDefinition()->getBasicDefinition()->getMode(); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $mode = $definition->getBasicDefinition()->getMode(); + if (BasicDefinitionInterface::MODE_HIERARCHICAL !== $mode) { return []; } foreach ((array) $this->varValue as $value) { $dataDriver = $environment->getDataProvider(); - $this->parentsOf($dataDriver->fetch($dataDriver->getEmptyConfig()->setId($value)), $parents); + assert($dataDriver instanceof DataProviderInterface); + + $model = $dataDriver->fetch($dataDriver->getEmptyConfig()->setId($value)); + assert($model instanceof ModelInterface); + + $this->parentsOf($model, $parents); } return $parents; @@ -1459,31 +1662,41 @@ private function determineParentsOfValues() */ private function handleInputNameForEditAll() { + $inputProvider = $this->getEnvironment()->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + if ( - ('select' !== $this->getEnvironment()->getInputProvider()->getParameter('act')) - && ('edit' !== $this->getEnvironment()->getInputProvider()->getParameter('select')) - && ('edit' !== $this->getEnvironment()->getInputProvider()->getParameter('mode')) + ('select' !== $inputProvider->getParameter('act')) + && ('edit' !== $inputProvider->getParameter('select')) + && ('edit' !== $inputProvider->getParameter('mode')) ) { return; } - $tableName = \explode('____', $this->getEnvironment()->getInputProvider()->getValue('name'))[0]; + $tableName = \explode('____', $inputProvider->getValue('name'))[0]; $sessionKey = 'DC_GENERAL_' . \strtoupper($tableName); - $sessionStorage = - System::getContainer()->get('cca.dc-general.session_factory')->createService()->setScope($sessionKey); + $sessionFactory = System::getContainer()->get('cca.dc-general.session_factory'); + assert($sessionFactory instanceof SessionStorageFactory); - $selectAction = $this->getEnvironment()->getInputProvider()->getParameter('select'); + $sessionStorage = $sessionFactory->createService(); + assert($sessionStorage instanceof SessionStorageInterface); + $sessionStorage->setScope($sessionKey); + $selectAction = $inputProvider->getParameter('select'); + + /** @var array{models: list} $session */ $session = $sessionStorage->get($tableName . '.' . $selectAction); + $propertyNamePrefix = ''; $originalPropertyName = null; - foreach ((array) $session['models'] as $modelId) { + + foreach ($session['models'] as $modelId) { if (null !== $originalPropertyName) { break; } $propertyNamePrefix = \str_replace('::', '____', $modelId) . '_'; - if (0 !== strpos($this->strName, $propertyNamePrefix)) { + if (0 !== \strpos($this->strName, $propertyNamePrefix)) { continue; } diff --git a/src/Contao/View/Contao2BackendView/TreeSelect.php b/src/Contao/View/Contao2BackendView/TreeSelect.php index f67c0f88..2a752d70 100644 --- a/src/Contao/View/Contao2BackendView/TreeSelect.php +++ b/src/Contao/View/Contao2BackendView/TreeSelect.php @@ -33,11 +33,17 @@ use Contao\System; use ContaoCommunityAlliance\DcGeneral\Contao\Compatibility\DcCompat; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Subscriber\WidgetBuilder; +use ContaoCommunityAlliance\DcGeneral\Data\DataProviderInterface; use ContaoCommunityAlliance\DcGeneral\Data\ModelId; +use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\DcGeneral; use ContaoCommunityAlliance\DcGeneral\Factory\DcGeneralFactory; +use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; +use ContaoCommunityAlliance\DcGeneral\SessionStorageInterface; use ContaoCommunityAlliance\Translator\Contao\LangArrayTranslator; use ContaoCommunityAlliance\Translator\TranslatorChain; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; /** * Class TreeSelect. @@ -45,16 +51,13 @@ * Back end tree picker for usage in generaltree.php. * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * + * @deprecated Do not use - here for legacy reasons only. + * + * @psalm-suppress PropertyNotSetInConstructor */ class TreeSelect { - /** - * Current ajax object. - * - * @var object - */ - protected $objAjax; - /** * The DcGeneral Object. * @@ -78,9 +81,11 @@ public function __construct() Config::getInstance(); Database::getInstance(); + /** @psalm-suppress DeprecatedMethod */ BackendUser::getInstance()->authenticate(); System::loadLanguageFile('default'); + /** @psalm-suppress DeprecatedMethod */ Backend::setStaticUrls(); } @@ -89,14 +94,20 @@ public function __construct() * * @return void * + * @throws \Exception * @SuppressWarnings(PHPMD.Superglobals) * @SuppressWarnings(PHPMD.CamelCaseVariableName) + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ public function run() { - $environment = $this->itemContainer->getEnvironment(); + $environment = $this->itemContainer->getEnvironment(); + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + $sessionStorage = $environment->getSessionStorage(); + assert($sessionStorage instanceof SessionStorageInterface); // Ajax request. // @codingStandardsIgnoreStart - We need POST access here. @@ -114,6 +125,7 @@ public function run() \define('CURRENT_ID', ($inputTable ? $sessionStorage->get('CURRENT_ID') : $inputId)); $dispatcher = System::getContainer()->get('event_dispatcher'); + assert($dispatcher instanceof EventDispatcherInterface); $translator = new TranslatorChain(); $translator->add(new LangArrayTranslator($dispatcher)); @@ -131,8 +143,10 @@ public function run() } // Merge with the information from the data container. - $property = $environment - ->getDataDefinition() + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $property = $definition ->getPropertiesDefinition() ->getProperty($inputField); $extra = $property->getExtra(); @@ -142,14 +156,19 @@ public function run() $property->setExtra(\array_merge($property->getExtra(), $information['eval'])); $dataProvider = $environment->getDataProvider(); - $model = $dataProvider->getEmptyModel(); + assert($dataProvider instanceof DataProviderInterface); + + $model = $dataProvider->getEmptyModel(); if ($inputProvider->getParameter('id')) { $modelId = ModelId::fromSerialized($inputProvider->getParameter('id')); $model = $dataProvider->fetch($dataProvider->getEmptyConfig()->setId($modelId->getId())); + assert($model instanceof ModelInterface); } + $widgetBuilder = new WidgetBuilder($environment); + /** @var \ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\TreePicker $treeSelector */ - $treeSelector = (new WidgetBuilder($environment))->buildWidget($property, $model); + $treeSelector = $widgetBuilder->buildWidget($property, $model); $treeSelector->value = \array_filter(\explode(',', $inputProvider->getParameter('value'))); // AJAX request. @@ -160,6 +179,7 @@ public function run() $template = new ContaoBackendViewTemplate('be_main'); + /** @psalm-suppress UndefinedMagicPropertyFetch */ $template ->set('isPopup', true) ->set('main', $treeSelector->generatePopup()) @@ -177,6 +197,10 @@ public function run() ->set('managerHref', ''); // Add the manager link. + /** + * @psalm-suppress UndefinedThisPropertyFetch + * @psalm-suppress UndefinedMagicPropertyFetch + */ if ($treeSelector->managerHref) { $template ->set('managerHref', 'contao?' . StringUtil::ampersand($treeSelector->managerHref) . '&popup=1'); diff --git a/src/Contao/View/Contao2BackendView/TreeView.php b/src/Contao/View/Contao2BackendView/TreeView.php index 3a05c6fb..9f143ecf 100644 --- a/src/Contao/View/Contao2BackendView/TreeView.php +++ b/src/Contao/View/Contao2BackendView/TreeView.php @@ -32,16 +32,21 @@ use ContaoCommunityAlliance\Contao\Bindings\Events\Backend\AddToUrlEvent; use ContaoCommunityAlliance\Contao\Bindings\Events\Image\GenerateHtmlEvent; use ContaoCommunityAlliance\DcGeneral\Action; +use ContaoCommunityAlliance\DcGeneral\Clipboard\ClipboardInterface; use ContaoCommunityAlliance\DcGeneral\Clipboard\Filter; use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminator; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\GetPasteRootButtonEvent; +use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\ModelToLabelEvent; +use ContaoCommunityAlliance\DcGeneral\Controller\ControllerInterface; use ContaoCommunityAlliance\DcGeneral\Controller\ModelCollector; use ContaoCommunityAlliance\DcGeneral\Controller\TreeCollector; use ContaoCommunityAlliance\DcGeneral\Controller\TreeNodeStates; use ContaoCommunityAlliance\DcGeneral\Data\CollectionInterface; +use ContaoCommunityAlliance\DcGeneral\Data\DataProviderInterface; use ContaoCommunityAlliance\DcGeneral\Data\ModelId; use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; use ContaoCommunityAlliance\DcGeneral\Data\MultiLanguageDataProviderInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\DcGeneralEvents; use ContaoCommunityAlliance\DcGeneral\DcGeneralViews; use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; @@ -50,8 +55,13 @@ use ContaoCommunityAlliance\DcGeneral\Event\ViewEvent; use ContaoCommunityAlliance\DcGeneral\EventListener\ModelRelationship\TreeEnforcingListener; use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralRuntimeException; +use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; use ContaoCommunityAlliance\DcGeneral\Panel\LimitElementInterface; +use ContaoCommunityAlliance\DcGeneral\Panel\PanelContainerInterface; use ContaoCommunityAlliance\DcGeneral\Panel\SortElementInterface; +use ContaoCommunityAlliance\DcGeneral\SessionStorageInterface; +use ContaoCommunityAlliance\Translator\TranslatorInterface; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface; @@ -94,6 +104,8 @@ public function __construct( if (null === $tokenManager) { $tokenManager = System::getContainer()->get('security.csrf.token_manager'); + assert($tokenManager instanceof CsrfTokenManagerInterface); + // @codingStandardsIgnoreStart @trigger_error( 'Not passing the csrf token manager as 2th argument to "' . __METHOD__ . '" is deprecated ' . @@ -104,6 +116,8 @@ public function __construct( } if (null === $tokenName) { $tokenName = System::getContainer()->getParameter('contao.csrf_token_name'); + assert(\is_string($tokenName)); + // @codingStandardsIgnoreStart @trigger_error( 'Not passing the csrf token name as 3th argument to "' . __METHOD__ . '" is deprecated ' . @@ -134,8 +148,13 @@ protected function getToggleId() */ protected function getTreeNodeStates() { - $sessionStorage = $this->getEnvironment()->getSessionStorage(); - $openElements = $sessionStorage->get($this->getToggleId()); + $environment = $this->getEnvironment(); + assert($environment instanceof EnvironmentInterface); + + $sessionStorage = $environment->getSessionStorage(); + assert($sessionStorage instanceof SessionStorageInterface); + + $openElements = $sessionStorage->get($this->getToggleId()); if (!\is_array($openElements)) { $openElements = []; @@ -153,7 +172,12 @@ protected function getTreeNodeStates() */ protected function saveTreeNodeStates(TreeNodeStates $states) { - $sessionStorage = $this->getEnvironment()->getSessionStorage(); + $environment = $this->getEnvironment(); + assert($environment instanceof EnvironmentInterface); + + $sessionStorage = $environment->getSessionStorage(); + assert($sessionStorage instanceof SessionStorageInterface); + $sessionStorage->set($this->getToggleId(), $states->getStates()); } @@ -166,7 +190,12 @@ protected function saveTreeNodeStates(TreeNodeStates $states) */ private function handleNodeStateChanges() { - $input = $this->getEnvironment()->getInputProvider(); + $environment = $this->getEnvironment(); + assert($environment instanceof EnvironmentInterface); + + $input = $environment->getInputProvider(); + assert($input instanceof InputProviderInterface); + if (($modelId = $input->getParameter('ptg')) && ($providerName = $input->getParameter('provider'))) { $states = $this->getTreeNodeStates(); // Check if the open/close all has been triggered or just a model. @@ -179,7 +208,7 @@ private function handleNodeStateChanges() $this->toggleModel($providerName, $modelId); } - ViewHelpers::redirectHome($this->environment); + ViewHelpers::redirectHome($environment); } } @@ -219,27 +248,40 @@ protected function isModelOpen($model) */ public function loadCollection($rootId = null, $level = 0, $providerName = null) { - $environment = $this->getEnvironment(); - $dataDriver = $environment->getDataProvider($providerName); + $environment = $this->getEnvironment(); + assert($environment instanceof EnvironmentInterface); + + $dataDriver = $environment->getDataProvider($providerName); + assert($dataDriver instanceof DataProviderInterface); + + $panel = $this->getPanel(); + assert($panel instanceof PanelContainerInterface); + $realProvider = $dataDriver->getEmptyModel()->getProviderName(); - $collector = new TreeCollector( + + /** @psalm-suppress DeprecatedMethod */ + $collector = new TreeCollector( $environment, - $this->getPanel(), + $panel, $this->getViewSection()->getListingConfig()->getDefaultSortingFields(), $this->getTreeNodeStates() ); + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + $collection = $rootId ? $collector->getTreeCollectionRecursive($rootId, $level, $realProvider) : $collector->getChildrenOf( $realProvider, $level, - $environment->getInputProvider()->hasParameter('pid') ? $this->loadParentModel() : null + $inputProvider->hasParameter('pid') ? $this->loadParentModel() : null ); if ($rootId) { $treeData = $dataDriver->getEmptyCollection(); $model = $collection->get(0); + assert($model instanceof ModelInterface); if (!$model->getMeta(ModelInterface::HAS_CHILDREN)) { return $treeData; @@ -268,8 +310,12 @@ public function loadCollection($rootId = null, $level = 0, $providerName = null) protected function loadParentModel() { $environment = $this->getEnvironment(); + assert($environment instanceof EnvironmentInterface); - if (!($parentId = $environment->getInputProvider()->getParameter('pid'))) { + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + + if (!($parentId = $inputProvider->getParameter('pid'))) { throw new DcGeneralRuntimeException( 'TreeView needs a proper parent id defined, somehow none is defined?', 1 @@ -318,23 +364,36 @@ protected function calcLabelFields($providerName) */ protected function parseModel($model, $toggleID) { - $event = new FormatModelLabelEvent($this->environment, $model); - $this->environment->getEventDispatcher()->dispatch($event, DcGeneralEvents::FORMAT_MODEL_LABEL); + $environment = $this->environment; + assert($environment instanceof EnvironmentInterface); + + $event = new FormatModelLabelEvent($environment, $model); + + $environment = $this->getEnvironment(); + assert($environment instanceof EnvironmentInterface); + + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $dispatcher->dispatch($event, DcGeneralEvents::FORMAT_MODEL_LABEL); $model->setMeta($model::LABEL_VALUE, $event->getLabel()); $template = $this->getTemplate('dcbe_general_treeview_entry'); + $translator = $environment->getTranslator(); + assert($translator instanceof TranslatorInterface); + if ($model->getMeta($model::SHOW_CHILDREN)) { - $toggleTitle = $this->getEnvironment()->getTranslator()->translate('MSC.collapseNode'); + $toggleTitle = $translator->translate('MSC.collapseNode'); } else { - $toggleTitle = $this->getEnvironment()->getTranslator()->translate('MSC.expandNode'); + $toggleTitle = $translator->translate('MSC.expandNode'); } $toggleUrlEvent = new AddToUrlEvent( 'ptg=' . $model->getId() . '&provider=' . $model->getProviderName() ); - $this->getEnvironment()->getEventDispatcher()->dispatch($toggleUrlEvent, ContaoEvents::BACKEND_ADD_TO_URL); + $dispatcher->dispatch($toggleUrlEvent, ContaoEvents::BACKEND_ADD_TO_URL); $toggleData = [ 'url' => \html_entity_decode($toggleUrlEvent->getUrl()), @@ -377,9 +436,12 @@ protected function generateTreeView($collection, $treeClass) { $content = []; + $environment = $this->environment; + assert($environment instanceof EnvironmentInterface); + // Generate buttons - only if not in select mode! if (!$this->isSelectModeActive()) { - (new ButtonRenderer($this->environment))->renderButtonsForCollection($collection); + (new ButtonRenderer($environment))->renderButtonsForCollection($collection); } foreach ($collection as $model) { @@ -420,17 +482,30 @@ protected function generateTreeView($collection, $treeClass) */ public static function renderPasteRootButton(GetPasteRootButtonEvent $event) { - if (null !== $event->getHtml()) { - return $event->getHtml(); + if (null !== ($button = $event->getHtml())) { + return $button; } + $environment = $event->getEnvironment(); - $label = $environment->getTranslator()->translate( + assert($environment instanceof EnvironmentInterface); + + $translator = $environment->getTranslator(); + assert($translator instanceof TranslatorInterface); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $label = $translator->translate( 'pasteinto.0', - $environment->getDataDefinition()->getName() + $definition->getName() ); + + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + if ($event->isPasteDisabled()) { /** @var GenerateHtmlEvent $imageEvent */ - $imageEvent = $environment->getEventDispatcher()->dispatch( + $imageEvent = $dispatcher->dispatch( new GenerateHtmlEvent( 'pasteinto_.svg', $label, @@ -439,11 +514,11 @@ public static function renderPasteRootButton(GetPasteRootButtonEvent $event) ContaoEvents::IMAGE_GET_HTML ); - return $imageEvent->getHtml(); + return $imageEvent->getHtml() ?? ''; } /** @var GenerateHtmlEvent $imageEvent */ - $imageEvent = $environment->getEventDispatcher()->dispatch( + $imageEvent = $dispatcher->dispatch( new GenerateHtmlEvent( 'pasteinto.svg', $label, @@ -457,7 +532,7 @@ public static function renderPasteRootButton(GetPasteRootButtonEvent $event) $event->getHref(), StringUtil::specialchars($label), 'onclick="Backend.getScrollOffset()"', - $imageEvent->getHtml() + $imageEvent->getHtml() ?? '' ); } @@ -467,14 +542,20 @@ public static function renderPasteRootButton(GetPasteRootButtonEvent $event) * @param CollectionInterface $collection The collection of items. * * @return string + * + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ protected function viewTree($collection) { $definition = $this->getDataDefinition(); $listing = $this->getViewSection()->getListingConfig(); $basicDefinition = $definition->getBasicDefinition(); - $environment = $this->getEnvironment(); - $dispatcher = $environment->getEventDispatcher(); + + $environment = $this->getEnvironment(); + assert($environment instanceof EnvironmentInterface); + + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); // Init some Vars switch (6) { @@ -496,19 +577,26 @@ protected function viewTree($collection) if (null === $listing->getRootIcon()) { $labelIcon = 'pagemounts.svg'; } else { - $labelIcon = $listing->getRootIcon(); + $labelIcon = $listing->getRootIcon() ?? ''; } $filter = new Filter(); - $filter->andModelIsFromProvider($basicDefinition->getDataProvider()); + + $dataProvider = $basicDefinition->getDataProvider(); + assert(\is_string($dataProvider)); + + $filter->andModelIsFromProvider($dataProvider); if ($parentProviderName = $basicDefinition->getParentDataProvider()) { $filter->andParentIsFromProvider($parentProviderName); } else { $filter->andHasNoParent(); } + $clipboard = $environment->getClipboard(); + assert($clipboard instanceof ClipboardInterface); + // Root paste into. - if ($environment->getClipboard()->isNotEmpty($filter)) { + if ($clipboard->isNotEmpty($filter)) { /** @var AddToUrlEvent $urlEvent */ $urlEvent = $dispatcher->dispatch( new AddToUrlEvent( @@ -520,7 +608,7 @@ protected function viewTree($collection) ContaoEvents::BACKEND_ADD_TO_URL ); - $buttonEvent = new GetPasteRootButtonEvent($this->getEnvironment()); + $buttonEvent = new GetPasteRootButtonEvent($environment); $buttonEvent ->setHref($urlEvent->getUrl()) ->setPasteDisabled(false); @@ -551,7 +639,7 @@ protected function viewTree($collection) $this->formActionForSelect($template); // Add breadcrumb, if we have one. - if (null !== ($breadcrumb = $this->breadcrumb())) { + if ('' !== ($breadcrumb = $this->breadcrumb())) { $template->set('breadcrumb', $breadcrumb); } @@ -568,12 +656,20 @@ protected function viewTree($collection) protected function formActionForSelect(ContaoBackendViewTemplate $template) { $environment = $this->getEnvironment(); - if (!$template->get('select') || ('select' !== $environment->getInputProvider()->getParameter('act'))) { + assert($environment instanceof EnvironmentInterface); + + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + + if (!$template->get('select') || ('select' !== $inputProvider->getParameter('act'))) { return; } + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + $actionUrlEvent = new AddToUrlEvent('select=properties'); - $environment->getEventDispatcher()->dispatch($actionUrlEvent, ContaoEvents::BACKEND_ADD_TO_URL); + $dispatcher->dispatch($actionUrlEvent, ContaoEvents::BACKEND_ADD_TO_URL); $template->set('action', $actionUrlEvent->getUrl()); } @@ -581,17 +677,22 @@ protected function formActionForSelect(ContaoBackendViewTemplate $template) /** * {@inheritDoc} * + * @param ModelInterface $model + * * @deprecated Use ContaoCommunityAlliance\DcGeneral\EventListener\ModelRelationship\TreeEnforcingListener * - * @see \ContaoCommunityAlliance\DcGeneral\EventListener\ModelRelationship\TreeEnforcingListener + * @see TreeEnforcingListener * * @return void */ public function enforceModelRelationship($model) { + $environment = $this->getEnvironment(); + assert($environment instanceof EnvironmentInterface); + // Fallback implementation. $listener = new TreeEnforcingListener(); - $listener->process(new EnforceModelRelationshipEvent($this->getEnvironment(), $model)); + $listener->process(new EnforceModelRelationshipEvent($environment, $model)); } /** @@ -600,7 +701,12 @@ public function enforceModelRelationship($model) public function showAll(Action $action) { $environment = $this->getEnvironment(); - if ($environment->getDataDefinition()->getBasicDefinition()->isEditOnlyMode()) { + assert($environment instanceof EnvironmentInterface); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + if ($definition->getBasicDefinition()->isEditOnlyMode()) { return $this->edit($action); } @@ -609,8 +715,11 @@ public function showAll(Action $action) $collection = $this->loadCollection(); $content = []; - $viewEvent = new ViewEvent($this->environment, $action, DcGeneralViews::CLIPBOARD, []); - $environment->getEventDispatcher()->dispatch($viewEvent, DcGeneralEvents::VIEW); + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $viewEvent = new ViewEvent($environment, $action, DcGeneralViews::CLIPBOARD, []); + $dispatcher->dispatch($viewEvent, DcGeneralEvents::VIEW); // A list with ignored panels. $ignoredPanels = [ @@ -618,7 +727,7 @@ public function showAll(Action $action) SortElementInterface::class ]; - $content['language'] = $this->languageSwitcher($this->environment); + $content['language'] = $this->languageSwitcher($environment); $content['panel'] = $this->panel($ignoredPanels); $content['buttons'] = $this->generateHeaderButtons(); $content['clipboard'] = $viewEvent->getResponse(); @@ -628,7 +737,7 @@ public function showAll(Action $action) } /** - * Execute the multi language support. + * Execute the multi-language support. * * @param EnvironmentInterface $environment The environment. * @@ -645,10 +754,16 @@ private function languageSwitcher(EnvironmentInterface $environment) /** @var MultiLanguageDataProviderInterface $dataProvider */ + $controller = $environment->getController(); + assert($controller instanceof ControllerInterface); + + $translator = $environment->getTranslator(); + assert($translator instanceof TranslatorInterface); + return $template - ->set('languages', $environment->getController()->getSupportedLanguages(null)) + ->set('languages', $controller->getSupportedLanguages(null)) ->set('language', $dataProvider->getCurrentLanguage()) - ->set('submit', $this->environment->getTranslator()->translate('MSC.showSelected')) + ->set('submit', $translator->translate('MSC.showSelected')) ->set('REQUEST_TOKEN', $this->tokenManager->getToken($this->tokenName)) ->parse(); } @@ -666,7 +781,11 @@ private function languageSwitcher(EnvironmentInterface $environment) */ public function handleAjaxCall() { - $input = $this->getEnvironment()->getInputProvider(); + $environment = $this->getEnvironment(); + assert($environment instanceof EnvironmentInterface); + + $input = $environment->getInputProvider(); + assert($input instanceof InputProviderInterface); if ('DcGeneralLoadSubTree' !== $input->getValue('action')) { parent::handleAjaxCall(); @@ -701,6 +820,8 @@ public function ajaxTreeView($rootId, $providerName, $level) $collection = $this->loadCollection($rootId, $level, $providerName); $treeClass = ''; + // The switch is already prepared for more modes. + /** @psalm-suppress TypeDoesNotContainType */ switch (6) { case 5: $treeClass = 'tree'; @@ -717,17 +838,27 @@ public function ajaxTreeView($rootId, $providerName, $level) } /** - * Get the the container of selections. + * Get the container of selections. * * @return array */ - private function getSelectContainer() + private function getSelectContainer(): array { $environment = $this->getEnvironment(); + assert($environment instanceof EnvironmentInterface); + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $sessionName = $definition->getName() . '.' . $inputProvider->getParameter('mode'); + + $sessionStorage = $environment->getSessionStorage(); + assert($sessionStorage instanceof SessionStorageInterface); - $sessionName = $environment->getDataDefinition()->getName() . '.' . $inputProvider->getParameter('mode'); - if (!$environment->getSessionStorage()->has($sessionName)) { + if (!$sessionStorage->has($sessionName)) { return []; } @@ -736,7 +867,7 @@ private function getSelectContainer() return []; } - $session = $environment->getSessionStorage()->get($sessionName); + $session = $sessionStorage->get($sessionName); if (!\array_key_exists($selectAction, $session)) { return []; } diff --git a/src/Contao/View/Contao2BackendView/ViewHelpers.php b/src/Contao/View/Contao2BackendView/ViewHelpers.php index c92d088e..ac64737e 100644 --- a/src/Contao/View/Contao2BackendView/ViewHelpers.php +++ b/src/Contao/View/Contao2BackendView/ViewHelpers.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2021 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -16,7 +16,8 @@ * @author David Molineus * @author Stefan Heimes * @author Sven Baumann - * @copyright 2013-2021 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -28,18 +29,23 @@ use ContaoCommunityAlliance\DcGeneral\Contao\DataDefinition\Definition\Contao2BackendViewDefinitionInterface; use ContaoCommunityAlliance\DcGeneral\Data\ConfigInterface; use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\Properties\PropertyInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\GroupAndSortingDefinitionInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\GroupAndSortingInformationInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\ListingConfigInterface; use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; +use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; use ContaoCommunityAlliance\DcGeneral\Panel\PanelContainerInterface; use ContaoCommunityAlliance\DcGeneral\Panel\PanelInterface; use ContaoCommunityAlliance\DcGeneral\Panel\SortElementInterface; use ContaoCommunityAlliance\DcGeneral\View\Event\RenderReadablePropertyValueEvent; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; /** * Helper class that provides static methods used in views. + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class ViewHelpers { @@ -48,24 +54,29 @@ class ViewHelpers * * @param EnvironmentInterface $environment The environment. * - * @return GroupAndSortingDefinitionInterface + * @return GroupAndSortingDefinitionInterface|null */ public static function getCurrentSorting(EnvironmentInterface $environment) { /** @var BackendViewInterface $view */ $view = $environment->getView(); - foreach ($view->getPanel() as $panel) { + $panelInterface = $view->getPanel(); + assert($panelInterface instanceof PanelContainerInterface); + + foreach ($panelInterface as $panel) { /** @var PanelInterface $panel */ $sort = $panel->getElement('sort'); - if ($sort) { - /** @var SortElementInterface $sort */ + if ($sort instanceof SortElementInterface) { return $sort->getSelectedDefinition(); } } + $dataDefinition = $environment->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + /** @var Contao2BackendViewDefinitionInterface $viewSection */ - $viewSection = $environment->getDataDefinition()->getDefinition(Contao2BackendViewDefinitionInterface::NAME); + $viewSection = $dataDefinition->getDefinition(Contao2BackendViewDefinitionInterface::NAME); $definition = $viewSection->getListingConfig()->getGroupAndSortingDefinition(); if ($definition->hasDefault()) { return $definition->getDefault(); @@ -76,25 +87,31 @@ public static function getCurrentSorting(EnvironmentInterface $environment) /** * {@inheritDoc} + * + * @return string|null */ public static function getManualSortingProperty(EnvironmentInterface $environment) { /** @var BackendViewInterface $view */ $view = $environment->getView(); + $panelInterface = $view->getPanel(); + assert($panelInterface instanceof PanelContainerInterface); + $definition = null; - foreach ($view->getPanel() as $panel) { + foreach ($panelInterface as $panel) { /** @var PanelInterface $panel */ $sort = $panel->getElement('sort'); - if ($sort) { - /** @var SortElementInterface $sort */ + if ($sort instanceof SortElementInterface) { $definition = $sort->getSelectedDefinition(); } } if (null === $definition) { + $dataDefinition = $environment->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + /** @var Contao2BackendViewDefinitionInterface $viewDefinition */ - $dataDefinition = $environment->getDataDefinition(); $viewDefinition = $dataDefinition->getDefinition(Contao2BackendViewDefinitionInterface::NAME); $groupAndSorting = $viewDefinition->getListingConfig()->getGroupAndSortingDefinition(); @@ -206,7 +223,10 @@ public static function getReadableFieldValue( $model->getProperty($property->getName()) ); - $environment->getEventDispatcher()->dispatch($event, $event::NAME); + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $dispatcher->dispatch($event, $event::NAME); if (null !== $event->getRendered()) { return $event->getRendered(); @@ -225,9 +245,10 @@ public static function getReadableFieldValue( public static function redirectHome(EnvironmentInterface $environment) { $input = $environment->getInputProvider(); + assert($input instanceof InputProviderInterface); - if ($input->hasParameter('table') && ($hasParentId = $input->hasParameter('pid'))) { - if ($hasParentId) { + if ($input->hasParameter('table')) { + if ($input->hasParameter('pid')) { $event = new RedirectEvent( \sprintf( 'contao?do=%s&table=%s&pid=%s', @@ -254,6 +275,9 @@ public static function redirectHome(EnvironmentInterface $environment) ); } - $environment->getEventDispatcher()->dispatch($event, ContaoEvents::CONTROLLER_REDIRECT); + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $dispatcher->dispatch($event, ContaoEvents::CONTROLLER_REDIRECT); } } diff --git a/src/Contao/View/Contao2BackendView/Widget/AbstractWidget.php b/src/Contao/View/Contao2BackendView/Widget/AbstractWidget.php index 4e227d98..bc083007 100644 --- a/src/Contao/View/Contao2BackendView/Widget/AbstractWidget.php +++ b/src/Contao/View/Contao2BackendView/Widget/AbstractWidget.php @@ -30,6 +30,10 @@ * Abstract widget class as base for dc general backend widgets. * * This widget is only prepared to run in DcCompat mode! + * + * @psalm-suppress PropertyNotSetInConstructor + * + * @property ?DcCompat $objDca */ abstract class AbstractWidget extends Widget { @@ -50,9 +54,9 @@ abstract class AbstractWidget extends Widget /** * The data Container. * - * @var DcCompat + * @var DcCompat|null */ - protected $dataContainer; + protected $dataContainer = null; /** * Create a new instance. @@ -64,7 +68,9 @@ public function __construct($attributes = null, DcCompat $dataContainer = null) { parent::__construct($attributes); - $this->dataContainer = $dataContainer ?: $this->objDca; + $dataContainer = $dataContainer ?: $this->objDca; + assert(null === $dataContainer || $dataContainer instanceof DcCompat); + $this->dataContainer = $dataContainer; } /** @@ -74,6 +80,7 @@ public function __construct($attributes = null, DcCompat $dataContainer = null) */ public function getEnvironment() { + assert($this->dataContainer instanceof DcCompat); return $this->dataContainer->getEnvironment(); } @@ -84,6 +91,7 @@ public function getEnvironment() */ public function getModel() { + assert($this->dataContainer instanceof DcCompat); return $this->dataContainer->getModel(); } } diff --git a/src/Contao/View/Contao2BackendView/Widget/FileTree.php b/src/Contao/View/Contao2BackendView/Widget/FileTree.php index 6b4b82ee..6cc9b552 100644 --- a/src/Contao/View/Contao2BackendView/Widget/FileTree.php +++ b/src/Contao/View/Contao2BackendView/Widget/FileTree.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2022 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -16,7 +16,7 @@ * @author Stefan Heimes * @author Ingolf Steinhardt * @author Sven Baumann - * @copyright 2013-2022 Contao Community Alliance. + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -25,9 +25,12 @@ use Contao\Config; use Contao\CoreBundle\Exception\ResponseException; +use Contao\CoreBundle\Image\ImageFactoryInterface; +use Contao\CoreBundle\Picker\PickerBuilderInterface; use Contao\DataContainer; use Contao\Environment; use Contao\File; +use Contao\FileSelector; use Contao\FilesModel; use Contao\Image; use Contao\Image\ResizeConfiguration; @@ -36,21 +39,33 @@ use Contao\System; use ContaoCommunityAlliance\DcGeneral\Contao\Compatibility\DcCompat; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\ContaoBackendViewTemplate; +use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; +use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; +use ContaoCommunityAlliance\Translator\TranslatorInterface; use Symfony\Component\HttpFoundation\Response; /** * File tree widget being compatible with the dc general. * - * @property string strField The field name. - * @property bool mandatory If true the field is required. - * @property bool multiple If true multiple values are allowed. - * @property bool isGallery If true the a image gallery is rendered. - * @property bool isDownloads If true only allowed download files are listed. + * @property string $strField The field name. + * @property bool $mandatory If true the field is required. + * @property bool $multiple If true multiple values are allowed. + * @property bool $isGallery If true the image gallery is rendered. + * @property bool $isDownloads If true only allowed download files are listed. + * @property string $fieldType Either 'radio' or 'checkbox'. + * @property bool|null $files Flag if files shall be shown. + * @property bool|null $filesOnly Flag if only files shall be selectable. + * @property string|null $path The root path. + * @property string|null $extensions The list of valid file extensions. * * @see https://github.com/contao/core/blob/master/system/modules/core/widgets/FileTree.php * * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * @psalm-suppress PropertyNotSetInConstructor + * + * @deprecated Deprecated since Contao 4.13 as the used FilePicker has been deprecated. Use the picker instead. */ class FileTree extends AbstractWidget { @@ -71,16 +86,16 @@ class FileTree extends AbstractWidget /** * The order field attribute name. * - * @var string. + * @var string */ - protected $orderField; + protected $orderField = ''; /** * The order field value. * - * @var array. + * @var null|list */ - protected $orderFieldValue; + protected $orderFieldValue = null; /** * The default width of the thumbnail. @@ -171,7 +186,9 @@ public function __set($strKey, $varValue) * * @param string $strKey The property name. * - * @return string The property value + * @return bool|int|null|object|string The property value + * + * @psalm-suppress ImplementedReturnTypeMismatch - Widget incorrectly annotates that it only returns string. */ public function __get($strKey) { @@ -199,7 +216,7 @@ public function __get($strKey) * * @param string $strKey The property name. * - * @return boolean True if the property exists + * @return bool True if the property exists */ public function __isset($strKey) { @@ -217,12 +234,12 @@ public function __isset($strKey) return isset($this->placeholderImage); default: - return parent::__get($strKey); + return (bool) parent::__get($strKey); } } /** - * Setup the file tree widget. + * Set up the file tree widget. * * @return void * @@ -234,75 +251,93 @@ private function setUp() // Load the fonts for the drag hint (see #4838) $GLOBALS['TL_CONFIG']['loadGoogleFonts'] = true; - $this->projectDir = System::getContainer()->getParameter('kernel.project_dir'); + $projectDir = System::getContainer()->getParameter('kernel.project_dir'); + assert(\is_string($projectDir)); + $this->projectDir = $projectDir; - if (!$this->dataContainer || !$this->orderField) { + if (!$this->orderField) { return; } - $value = $this->dataContainer->getModel()->getProperty($this->orderField); + assert($this->dataContainer instanceof DcCompat); + $model = $this->dataContainer->getModel(); + assert($model instanceof ModelInterface); + + $value = $model->getProperty($this->orderField); // support serialized values. if (!\is_array($value)) { $value = StringUtil::deserialize($value, true); + assert(\is_array($value)); } + $value = \array_values($value); + /** @var list $value */ - $this->orderId = $this->orderField . \str_replace($this->strField, '', $this->strId); - $this->orderFieldValue = (!empty($value) && \is_array($value)) ? \array_filter($value) : []; + $this->orderId = $this->orderField . \str_replace($this->strField, '', (string) $this->strId); + $this->orderFieldValue = \array_values(\array_filter($value)); } /** * Process the validation. * - * @param mixed $inputValue The input value. + * @param mixed $varInput The input value. * * @return array|string */ - protected function validator($inputValue) + protected function validator($varInput) { - if ('' === $inputValue) { + if ('' === $varInput) { if ($this->mandatory) { + $translator = $this->getEnvironment()->getTranslator(); + assert($translator instanceof TranslatorInterface); + $this->addError( - $this->getEnvironment()->getTranslator()->translate('mandatory', 'ERR', [$this->strLabel]) + $translator->translate('mandatory', 'ERR', [$this->strLabel]) ); } return ''; } - $inputValue = \array_map('\Contao\StringUtil::uuidToBin', \array_filter(\explode(',', $inputValue))); + $varInput = \array_map('\Contao\StringUtil::uuidToBin', \array_filter(\explode(',', $varInput))); - return $this->multiple ? $inputValue : $inputValue[0]; + return $this->multiple ? $varInput : $varInput[0]; } /** * Render the file list. * * @param array $icons The generated icons. - * @param Collection|null $collection The files collection. + * @param Collection|null $collection The file's collection. * - * @param bool $followSubDirs If true subfolders get rendered. + * @param bool $followSubDirs If true sub-folders get rendered. * * @return void */ - private function renderList(array &$icons, Collection $collection = null, $followSubDirs = false) + private function renderList(array &$icons, Collection $collection = null, bool $followSubDirs = false) { if (!$collection) { return; } foreach ($collection->getModels() as $model) { + assert($model instanceof FilesModel); + $uuid = $model->uuid; + assert(is_string($uuid)); // File system and database seem not in sync if (!\file_exists($this->projectDir . '/' . $model->path)) { continue; } if (('folder' === $model->type) && $followSubDirs) { - $this->renderList($icons, FilesModel::findByPid($model->uuid)); + $files = FilesModel::findByPid($uuid); + assert($files instanceof Collection); + $this->renderList($icons, $files); continue; } + if (false !== ($icon = $this->renderIcon($model, $this->isGallery, $this->isDownloads))) { - $icons[\md5($model->uuid)] = ['uuid' => $model->uuid, 'image' => $icon]; + $icons[\md5($uuid)] = ['uuid' => $uuid, 'image' => $icon]; } } } @@ -397,14 +432,16 @@ private function generateGalleryImage(File $file, $info) ) ) { // Inline the image if no preview image will be generated (see #636). - if ( - $file->height !== null && $file->height <= $this->thumbnailHeight - && $file->width !== null && $file->width <= $this->thumbnailWidth - ) { + if ($file->height <= $this->thumbnailHeight && $file->width <= $this->thumbnailWidth) { $image = $file->dataUri; } else { $projectDir = System::getContainer()->getParameter('kernel.project_dir'); - $image = System::getContainer()->get('contao.image.image_factory')->create( + assert(\is_string($projectDir)); + + $imageFactory = System::getContainer()->get('contao.image.image_factory'); + assert($imageFactory instanceof ImageFactoryInterface); + + $image = $imageFactory->create( $projectDir . '/' . $file->path, [$this->thumbnailWidth, $this->thumbnailHeight, ResizeConfiguration::MODE_BOX] )->getUrl($projectDir); @@ -468,15 +505,18 @@ private function generateLink() $extras['filesOnly'] = (bool) $this->filesOnly; } - if ($this->path) { - $extras['path'] = (string) $this->path; + if (\is_string($this->path)) { + $extras['path'] = $this->path; } - if ($this->extensions) { - $extras['extensions'] = (string) $this->extensions; + if (\is_string($this->extensions)) { + $extras['extensions'] = $this->extensions; } - return System::getContainer()->get('contao.picker.builder')->getUrl('file', $extras); + $pickerBuilder = System::getContainer()->get('contao.picker.builder'); + assert($pickerBuilder instanceof PickerBuilderInterface); + + return $pickerBuilder->getUrl('file', $extras); } /** @@ -489,8 +529,12 @@ public function generate() if (!empty($this->varValue)) { $files = FilesModel::findMultipleByUuids((array) $this->varValue); - $this->renderList($icons, $files, $this->isGallery || $this->isDownloads); - //dump($icons); + + if (null !== $files) { + assert($files instanceof Collection); + $this->renderList($icons, $files, $this->isGallery || $this->isDownloads); + } + $icons = $this->applySorting($icons); // Files can be null. @@ -501,12 +545,15 @@ public function generate() } } + $translator = $this->getEnvironment()->getTranslator(); + assert($translator instanceof TranslatorInterface); + $content = (new ContaoBackendViewTemplate($this->subTemplate)) - ->setTranslator($this->getEnvironment()->getTranslator()) + ->setTranslator($translator) ->set('name', $this->strName) ->set('id', $this->strId) ->set('value', \implode(',', $values)) - ->set('hasOrder', $this->orderField != '' && \is_array($this->orderFieldValue)) + ->set('hasOrder', $this->orderField !== '' && \is_array($this->orderFieldValue)) ->set('icons', $icons) ->set('isGallery', $this->isGallery) ->set('orderId', $this->orderId) @@ -538,14 +585,20 @@ public function updateAjax($ajaxAction, DataContainer $dataContainer) return ''; } + assert($dataContainer instanceof DcCompat); $this->dataContainer = $dataContainer; $this->setUp(); - $environment = $this->dataContainer->getEnvironment(); + $environment = $this->dataContainer->getEnvironment(); + $dataDefinition = $environment->getDataDefinition(); - $inputProvider = $environment->getInputProvider(); - $propertyName = $inputProvider->getValue('name'); - $information = (array) $GLOBALS['TL_DCA'][$dataContainer->getName()]['fields'][$propertyName]; + assert($dataDefinition instanceof ContainerInterface); + + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + + $propertyName = $inputProvider->getValue('name'); + $information = (array) $GLOBALS['TL_DCA'][$dataContainer->getName()]['fields'][$propertyName]; // Merge with the information from the data container. $information['eval'] = \array_merge( @@ -555,10 +608,11 @@ public function updateAjax($ajaxAction, DataContainer $dataContainer) $combat = new DcCompat($environment, null, $propertyName); - /** @var \FileSelector $widgetClass */ + /** @var class-string $widgetClass */ + /** @psalm-suppress DeprecatedClass - we know we are deprecated ourselves. :D */ $widgetClass = $GLOBALS['BE_FFL']['fileSelector']; - /** @var \FileSelector $widget */ + /** @psalm-suppress UnsafeInstantiation - no better way to instantiate :( */ $widget = new $widgetClass( $widgetClass::getAttributesFromDca( $information, diff --git a/src/Contao/View/Contao2BackendView/Widget/FileTreeOrder.php b/src/Contao/View/Contao2BackendView/Widget/FileTreeOrder.php index 92c8567e..c2559a48 100644 --- a/src/Contao/View/Contao2BackendView/Widget/FileTreeOrder.php +++ b/src/Contao/View/Contao2BackendView/Widget/FileTreeOrder.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -15,7 +15,8 @@ * @author Sven Baumann * @author Christian Schiffler * @author Stefan Heimes - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -26,6 +27,8 @@ * This widget is a supporting widget to store the file tree orderings. * * The ContaoWidgetManager does not allow input values without a widget. This is used as helper widget instead. + * + * @psalm-suppress PropertyNotSetInConstructor */ class FileTreeOrder extends AbstractWidget { @@ -39,11 +42,9 @@ class FileTreeOrder extends AbstractWidget /** * {@inheritdoc} */ - protected function validator($inputValue) + protected function validator($varInput) { - $inputValue = \array_map('\Contao\StringUtil::uuidToBin', \array_filter(\explode(',', $inputValue))); - - return $inputValue; + return \array_map('\Contao\StringUtil::uuidToBin', \array_filter(\explode(',', $varInput))); } /** diff --git a/src/Contao/View/Contao2BackendView/Widget/PageTree.php b/src/Contao/View/Contao2BackendView/Widget/PageTree.php index a267c700..b57a30d0 100644 --- a/src/Contao/View/Contao2BackendView/Widget/PageTree.php +++ b/src/Contao/View/Contao2BackendView/Widget/PageTree.php @@ -20,14 +20,20 @@ namespace ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Widget; +use Contao\CoreBundle\Picker\PickerBuilderInterface; use Contao\System; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\TreePicker; use ContaoCommunityAlliance\DcGeneral\Data\CollectionInterface; +use ContaoCommunityAlliance\DcGeneral\Data\DataProviderInterface; +use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; +use ContaoCommunityAlliance\Translator\TranslatorInterface; /** * Page tree widget being compatible with the dc general. * * @see https://github.com/contao/core/blob/master/system/modules/core/widgets/PageTree.php + * + * @psalm-suppress PropertyNotSetInConstructor */ class PageTree extends TreePicker { @@ -41,15 +47,17 @@ class PageTree extends TreePicker /** * Process the validation. * - * @param mixed $inputValue The input value. + * @param mixed $varInput The input value. + * + * @return null|string|list * - * @return array|string */ - protected function validator($inputValue) + protected function validator($varInput) { $translator = $this->getEnvironment()->getTranslator(); + assert($translator instanceof TranslatorInterface); - $widgetValue = $this->widgetToValue($inputValue); + $widgetValue = $this->widgetToValue($varInput); if ((null === $widgetValue) && $this->mandatory) { $this->addError($translator->translate('mandatory', 'ERR', [$this->strLabel])); } @@ -70,9 +78,14 @@ public function loadCollection($rootId = null, $level = 0, $providerName = null) { $collection = $this->getTreeCollectionRecursive($rootId, $level, $providerName); - $treeData = $this->getEnvironment()->getDataProvider($providerName)->getEmptyCollection(); + $dataProvider = $this->getEnvironment()->getDataProvider($providerName); + assert($dataProvider instanceof DataProviderInterface); + + $treeData = $dataProvider->getEmptyCollection(); if ($rootId) { $objModel = $collection->get(0); + assert($objModel instanceof ModelInterface); + foreach ($objModel->getMeta($objModel::CHILD_COLLECTIONS) as $childCollection) { foreach ($childCollection as $subModel) { $treeData->push($subModel); @@ -103,8 +116,9 @@ protected function generatePickerUrl() 'fieldType' => $this->fieldType ]; - return System::getContainer() - ->get('contao.picker.builder') - ->getUrl('page', $extra); + $pickerBuilder = System::getContainer()->get('contao.picker.builder'); + assert($pickerBuilder instanceof PickerBuilderInterface); + + return $pickerBuilder->getUrl('page', $extra); } } diff --git a/src/Contao/View/Contao2BackendView/Widget/PageTreeOrder.php b/src/Contao/View/Contao2BackendView/Widget/PageTreeOrder.php index dcfea41e..21e13c6c 100644 --- a/src/Contao/View/Contao2BackendView/Widget/PageTreeOrder.php +++ b/src/Contao/View/Contao2BackendView/Widget/PageTreeOrder.php @@ -23,6 +23,8 @@ * This widget is a supporting widget to store the page tree orderings. * * The ContaoWidgetManager does not allow input values without a widget. This is used as helper widget instead. + * + * @psalm-suppress PropertyNotSetInConstructor */ class PageTreeOrder extends AbstractWidget { diff --git a/src/Contao/View/Contao2BackendView/Widget/TreePickerOrder.php b/src/Contao/View/Contao2BackendView/Widget/TreePickerOrder.php index ac9e77d1..9dd81a68 100644 --- a/src/Contao/View/Contao2BackendView/Widget/TreePickerOrder.php +++ b/src/Contao/View/Contao2BackendView/Widget/TreePickerOrder.php @@ -23,6 +23,8 @@ * This widget is a supporting widget to store the page tree orderings. * * The ContaoWidgetManager does not allow input values without a widget. This is used as helper widget instead. + * + * @psalm-suppress PropertyNotSetInConstructor */ class TreePickerOrder extends AbstractWidget { @@ -45,9 +47,9 @@ public function generate() /** * {@inheritdoc} */ - protected function validator($inputValue) + protected function validator($varInput) { - return \array_filter(\explode(',', $inputValue)); + return \array_filter(\explode(',', $varInput)); } /** diff --git a/src/ContaoManager/Plugin.php b/src/ContaoManager/Plugin.php index a71c8568..e9f77c11 100644 --- a/src/ContaoManager/Plugin.php +++ b/src/ContaoManager/Plugin.php @@ -29,6 +29,7 @@ use ContaoCommunityAlliance\Contao\Bindings\CcaEventsContaoBindingsBundle; use ContaoCommunityAlliance\DcGeneral\CcaDcGeneralBundle; use ContaoCommunityAlliance\UrlBuilder\CcaUrlBuilderBundle; +use Symfony\Component\Config\Loader\LoaderInterface; use Symfony\Component\Config\Loader\LoaderResolverInterface; use Symfony\Component\HttpKernel\KernelInterface; @@ -59,8 +60,8 @@ public function getBundles(ParserInterface $parser) */ public function getRouteCollection(LoaderResolverInterface $resolver, KernelInterface $kernel) { - return $resolver - ->resolve(__DIR__ . '/../Resources/config/routing.yml') - ->load(__DIR__ . '/../Resources/config/routing.yml'); + $loader = $resolver->resolve(__DIR__ . '/../Resources/config/routing.yml'); + assert($loader instanceof LoaderInterface); + return $loader->load(__DIR__ . '/../Resources/config/routing.yml'); } } diff --git a/src/Controller/Ajax.php b/src/Controller/Ajax.php index e32f2cbb..d11c442d 100644 --- a/src/Controller/Ajax.php +++ b/src/Controller/Ajax.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -27,25 +28,32 @@ use Contao\Database; use Contao\System; use ContaoCommunityAlliance\DcGeneral\Contao\Compatibility\DcCompat; +use ContaoCommunityAlliance\DcGeneral\Data\DataProviderInterface; use ContaoCommunityAlliance\DcGeneral\Data\ModelId; use ContaoCommunityAlliance\DcGeneral\DataContainerInterface; +use ContaoCommunityAlliance\DcGeneral\DC\General; use ContaoCommunityAlliance\DcGeneral\EnvironmentAwareInterface; +use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBagInterface; +use Symfony\Component\HttpFoundation\Session\SessionInterface; /** * Class Ajax - General purpose Ajax handler for "executePostActions" as we can not use the default Contao * handling. * * See Contao core issue #5957. https://github.com/contao/core/pull/5957 + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ abstract class Ajax implements EnvironmentAwareInterface { /** * The data container calling us. * - * @var DataContainerInterface + * @var null|DataContainerInterface */ - protected $objDc; + protected $objDc = null; /** * Create a new instance. @@ -58,10 +66,13 @@ public function __construct() /** * Get the data container. * - * @return DataContainerInterface. + * @return General */ protected function getDataContainer() { + if (!$this->objDc instanceof General) { + throw new \LogicException('Must be set first via ' . self::class . '::executePostActions().'); + } return $this->objDc; } @@ -83,7 +94,10 @@ public function getEnvironment() */ protected function getGet($key, $decodeEntities = false) { - return $this->getEnvironment()->getInputProvider()->getParameter($key, $decodeEntities); + $inputProvider = $this->getEnvironment()->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + + return $inputProvider->getParameter($key, $decodeEntities); } /** @@ -92,11 +106,18 @@ protected function getGet($key, $decodeEntities = false) * @param string $key The key to retrieve. * @param bool $decodeEntities Decode the entities. * - * @return mixed + * @return string|null */ protected function getPost($key, $decodeEntities = false) { - return $this->getEnvironment()->getInputProvider()->getValue($key, $decodeEntities); + $inputProvider = $this->getEnvironment()->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + + $value = $inputProvider->getValue($key, $decodeEntities); + if ($value === null) { + return null; + } + return (string) $value; } /** @@ -108,7 +129,7 @@ protected function getPost($key, $decodeEntities = false) */ protected function getAjaxId() { - return preg_replace('/.*_([0-9a-zA-Z]+)$/', '$1', $this->getPost('id')); + return preg_replace('/.*_([0-9a-zA-Z]+)$/', '$1', (string) $this->getPost('id')); } /** @@ -116,13 +137,16 @@ protected function getAjaxId() * * This method exits the script! * - * @return void + * @return never * * @throws ResponseException Throws a response exception. + * + * @deprecated */ protected function loadStructure() { // Method ajaxTreeView is in TreeView.php - watch out! + /** @psalm-suppress DeprecatedMethod */ $response = new Response( $this->getDataContainer()->ajaxTreeView($this->getAjaxId(), (int) $this->getPost('level')) ); @@ -135,7 +159,7 @@ protected function loadStructure() * * This method exits the script! * - * @return void + * @return never * * @throws ResponseException Throws a response exception. */ @@ -206,7 +230,10 @@ public function executePostActions(DataContainerInterface $container) $this->objDc = $container; - $action = $this->getEnvironment()->getInputProvider()->getValue('action'); + $inputProvider = $this->getEnvironment()->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + + $action = $inputProvider->getValue('action'); if ( \in_array( @@ -258,16 +285,18 @@ public function executePostActions(DataContainerInterface $container) private function getActiveModel() { $input = $this->getEnvironment()->getInputProvider(); + assert($input instanceof InputProviderInterface); + if (false === $input->hasParameter('id')) { return null; } - $modelId = ModelId::fromSerialized($input->getParameter('id')); - $dataProvider = $this->getEnvironment()->getDataProvider($modelId->getDataProviderName()); + $modelId = ModelId::fromSerialized($input->getParameter('id')); - $model = $dataProvider->fetch($dataProvider->getEmptyConfig()->setId($modelId->getId())); + $dataProvider = $this->getEnvironment()->getDataProvider($modelId->getDataProviderName()); + assert($dataProvider instanceof DataProviderInterface); - return $model; + return $dataProvider->fetch($dataProvider->getEmptyConfig()->setId($modelId->getId())); } /** @@ -275,7 +304,7 @@ private function getActiveModel() * * Will get called from subclasses to have a central endpoint to exit the script. * - * @return void + * @return never * * @deprecated Deperecated since 2.1 and where remove in 3.0. Use own response exit. * @@ -287,8 +316,13 @@ protected function exitScript() @\trigger_error('Use own response exit!', E_USER_DEPRECATED); // @codingStandardsIgnoreEnd - $session = System::getContainer()->get('session'); - $sessionBag = $session->getBag('contao_backend')->all(); + $session = System::getContainer()->get('session'); + assert($session instanceof SessionInterface); + + $sessionBag = $session->getBag('contao_backend'); + assert($sessionBag instanceof AttributeBagInterface); + + $sessionBag = $sessionBag->all(); $user = BackendUser::getInstance(); diff --git a/src/Controller/Ajax3X.php b/src/Controller/Ajax3X.php index af0a2182..5ddf5824 100644 --- a/src/Controller/Ajax3X.php +++ b/src/Controller/Ajax3X.php @@ -27,17 +27,21 @@ use Contao\CoreBundle\Exception\ResponseException; use Contao\Dbafs; +use Contao\FileSelector; use Contao\PageSelector; use Contao\StringUtil; use Contao\Widget; use ContaoCommunityAlliance\Contao\Bindings\ContaoEvents; use ContaoCommunityAlliance\Contao\Bindings\Events\System\LogEvent; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\ContaoWidgetManager; +use ContaoCommunityAlliance\DcGeneral\Data\DataProviderInterface; use ContaoCommunityAlliance\DcGeneral\Data\ModelId; use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; use ContaoCommunityAlliance\DcGeneral\Data\PropertyValueBag; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; use ContaoCommunityAlliance\DcGeneral\SessionStorageInterface; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\HttpFoundation\Response; /** @@ -51,23 +55,29 @@ class Ajax3X extends Ajax /** * Get the widget instance. * - * @param string $fieldName The property name. - * @param string $serializedId The serialized id of the model. - * @param string $propertyValue The property value. + * @param string $fieldName The property name. + * @param string|null $serializedId The serialized id of the model. + * @param string $propertyValue The property value. * - * @return Widget + * @return Widget|null */ protected function getWidget($fieldName, $serializedId, $propertyValue) { - $environment = $this->getEnvironment(); - $property = $environment->getDataDefinition()->getPropertiesDefinition()->getProperty($fieldName); + $environment = $this->getEnvironment(); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $property = $definition->getPropertiesDefinition()->getProperty($fieldName); $propertyValues = new PropertyValueBag(); if (null !== $serializedId) { $model = $this->getModelFromSerializedId($serializedId); } else { $dataProvider = $environment->getDataProvider(); - $model = $dataProvider->getEmptyModel(); + assert($dataProvider instanceof DataProviderInterface); + + $model = $dataProvider->getEmptyModel(); } $widgetManager = new ContaoWidgetManager($environment, $model); @@ -77,10 +87,10 @@ protected function getWidget($fieldName, $serializedId, $propertyValue) $propertyValue = $this->getTreeValue($treeType, $propertyValue); if (('file' === $treeType) || ('page' === $treeType)) { $extra = $property->getExtra(); - if (\is_array($propertyValue) && !isset($extra['multiple'])) { + if (!isset($extra['multiple'])) { $propertyValue = $propertyValue[0]; } else { - $propertyValue = \implode(',', (array) $propertyValue); + $propertyValue = \implode(',', $propertyValue); } } @@ -102,17 +112,20 @@ protected function getWidget($fieldName, $serializedId, $propertyValue) protected function loadPagetree() { $environment = $this->getEnvironment(); - $input = $environment->getInputProvider(); - $session = $environment->getSessionStorage(); + $input = $environment->getInputProvider(); assert($input instanceof InputProviderInterface); + $session = $environment->getSessionStorage(); assert($session instanceof SessionStorageInterface); - $field = $input->getValue('field'); - $name = $input->getValue('name'); - $level = (int) $input->getValue('level'); - $rootId = $input->getValue('id'); + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $field = $input->getValue('field'); + $name = $input->getValue('name'); + $level = (int) $input->getValue('level'); + $rootId = (string) $input->getValue('id'); $ajaxId = \preg_replace('/.*_([0-9a-zA-Z]+)$/', '$1', $rootId); $ajaxKey = \str_replace('_' . $ajaxId, '', $rootId); @@ -126,14 +139,20 @@ protected function loadPagetree() $nodes[$ajaxId] = (int) $input->getValue('state'); $session->set($ajaxKey, $nodes); - $arrData['strTable'] = $environment->getDataDefinition()->getName(); - $arrData['id'] = $ajaxName ?: $rootId; - $arrData['name'] = $name; + $arrData = [ + 'strTable' => $definition->getName(), + 'id' => $ajaxName ?: $rootId, + 'name' => $name, + ]; - /** @var PageSelector $widget */ + /** + * @psalm-suppress DeprecatedClass + * @var PageSelector $widget + */ $widget = new $GLOBALS['BE_FFL']['pageSelector']($arrData, $this->getDataContainer()); $widget->value = $this->getTreeValue('page', $input->getValue('value')); + /** @psalm-suppress InvalidArgument - rather pass it "as is", we do not trust Contao annotations. */ $response = new Response($widget->generateAjax($ajaxId, $field, $level)); throw new ResponseException($response); @@ -150,20 +169,30 @@ protected function loadPagetree() protected function loadFiletree() { $environment = $this->getEnvironment(); - $input = $environment->getInputProvider(); - $folder = $input->getValue('folder'); - $field = $input->getValue('field'); - $level = (int) $input->getValue('level'); + $input = $environment->getInputProvider(); + assert($input instanceof InputProviderInterface); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $folder = $input->getValue('folder'); + $field = $input->getValue('field'); + $level = (int) $input->getValue('level'); + + $arrData = []; $arrData['strTable'] = $input->getParameter('table'); $arrData['id'] = $field; $arrData['name'] = $field; $arrData = \array_merge( - $environment->getDataDefinition()->getPropertiesDefinition()->getProperty($field)->getExtra(), + $definition->getPropertiesDefinition()->getProperty($field)->getExtra(), $arrData ); - /** @var \FileSelector $widget */ + /** + * @psalm-suppress DeprecatedClass + * @var FileSelector $widget + */ $widget = new $GLOBALS['BE_FFL']['fileSelector']($arrData, $this->getDataContainer()); $widget->value = $this->getTreeValue($field, $input->getValue('value')); @@ -185,23 +214,26 @@ protected function loadFiletree() * @param string $type Either "page" or "file". * @param string $value The value as comma separated list. * - * @return string The value array. + * @return list The value array. */ protected function getTreeValue($type, $value) { // Convert the selected values. - if ('' !== $value) { - $value = StringUtil::trimsplit("\t", $value); - - // Automatically add resources to the DBAFS. - if ('file' === $type) { - foreach ($value as $k => $v) { - $value[$k] = StringUtil::binToUuid(Dbafs::addResource(\urldecode($v))->uuid); - } + if ('' === $value) { + return []; + } + $value = StringUtil::trimsplit("\t", $value); + + // Automatically add resources to the DBAFS. + if ('file' === $type) { + foreach ($value as $k => $v) { + $uuid = Dbafs::addResource(\urldecode($v))->uuid; + assert(\is_string($uuid)); + $value[$k] = StringUtil::binToUuid($uuid); } } - return $value; + return array_values($value); } /** @@ -219,16 +251,25 @@ protected function getModelFromSerializedId($serializedId) { $modelId = ModelId::fromSerialized($serializedId); $dataProvider = $this->getEnvironment()->getDataProvider($modelId->getDataProviderName()); - $model = $dataProvider->fetch($dataProvider->getEmptyConfig()->setId($modelId->getId())); + assert($dataProvider instanceof DataProviderInterface); + + $model = $dataProvider->fetch($dataProvider->getEmptyConfig()->setId($modelId->getId())); if (null === $model) { + $definition = $this->getEnvironment()->getDataDefinition(); + assert($definition instanceof ContainerInterface); + $event = new LogEvent( 'A record with the ID "' . $serializedId . '" does not exist in "' . - $this->getEnvironment()->getDataDefinition()->getName() . '"', + $definition->getName() . '"', 'Ajax executePostActions()', 'ERROR' ); - $this->getEnvironment()->getEventDispatcher()->dispatch($event, ContaoEvents::SYSTEM_LOG); + + $dispatcher = $this->getEnvironment()->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $dispatcher->dispatch($event, ContaoEvents::SYSTEM_LOG); throw new ResponseException(new Response(Response::$statusTexts[400], 400)); } @@ -239,23 +280,33 @@ protected function getModelFromSerializedId($serializedId) /** * Reload the file tree. * - * @return void + * @return never * * @throws ResponseException Throws a response exception. */ protected function reloadTree() { - $input = $this->getEnvironment()->getInputProvider(); + $input = $this->getEnvironment()->getInputProvider(); + assert($input instanceof InputProviderInterface); + $serializedId = ($input->hasParameter('id') && $input->getParameter('id')) ? $input->getParameter('id') : null; - $value = $input->hasValue('value') ? $input->getValue('value', true) : null; + $value = $input->hasValue('value') ? $input->getValue('value', true) : ''; + + $fieldName = $this->getFieldName(); + assert(\is_string($fieldName)); + + $widget = $this->getWidget($fieldName, $serializedId, $value); + assert($widget instanceof Widget); - $this->generateWidget($this->getWidget($this->getFieldName(), $serializedId, $value)); + $this->generateWidget($widget); throw new ResponseException(new Response('')); } /** * {@inheritDoc} + * + * @return never */ protected function reloadPagetree() { @@ -264,6 +315,8 @@ protected function reloadPagetree() /** * {@inheritDoc} + * + * @return never */ protected function reloadFiletree() { @@ -274,13 +327,20 @@ protected function reloadFiletree() * {@inheritDoc} * * @throws ResponseException Throws a response exception. + * + * @return never */ protected function setLegendState() { $environment = $this->getEnvironment(); - $input = $environment->getInputProvider(); - $session = $environment->getSessionStorage(); - $states = $session->get('LEGENDS'); + + $input = $environment->getInputProvider(); + assert($input instanceof InputProviderInterface); + + $session = $environment->getSessionStorage(); + assert($session instanceof SessionStorageInterface); + + $states = $session->get('LEGENDS'); $states[$input->getValue('table')][$input->getValue('legend')] = (bool) $input->getValue('state'); $session->set('LEGENDS', $states); @@ -297,6 +357,7 @@ private function getFieldName() { $environment = $this->getEnvironment(); $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); $fieldName = $inputProvider->hasValue('name') ? $inputProvider->getValue('name') : null; if (null === $fieldName) { @@ -308,8 +369,12 @@ private function getFieldName() } $dataDefinition = $environment->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + $sessionStorage = $environment->getSessionStorage(); + assert($sessionStorage instanceof SessionStorageInterface); + /** @var array{models: list} $session */ $session = $sessionStorage->get($dataDefinition->getName() . '.' . $inputProvider->getParameter('select')); $originalPropertyName = null; @@ -344,6 +409,7 @@ private function generateWidget(Widget $widget) { $environment = $this->getEnvironment(); $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); if (('select' !== $inputProvider->getParameter('act')) && ('edit' !== $inputProvider->getParameter('mode'))) { echo $widget->parse(); @@ -351,9 +417,15 @@ private function generateWidget(Widget $widget) return; } - $model = $environment->getDataProvider()->getEmptyModel(); + $dataProvider = $environment->getDataProvider(); + assert($dataProvider instanceof DataProviderInterface); + + $model = $dataProvider->getEmptyModel(); $model->setProperty($widget->name, $widget->value); - echo (new ContaoWidgetManager($environment, $model))->getWidget($inputProvider->getValue('name'))->parse(); + $widget = (new ContaoWidgetManager($environment, $model))->getWidget($inputProvider->getValue('name')); + assert($widget instanceof Widget); + + echo $widget->parse(); } } diff --git a/src/Controller/BackendTreeController.php b/src/Controller/BackendTreeController.php index a1555437..4d8f2a7f 100644 --- a/src/Controller/BackendTreeController.php +++ b/src/Controller/BackendTreeController.php @@ -24,6 +24,9 @@ use Contao\Backend; use Contao\Config; +use Contao\Controller; +use Contao\CoreBundle\Framework\ContaoFramework; +use Contao\CoreBundle\Picker\PickerBuilderInterface; use Contao\CoreBundle\Picker\PickerInterface; use Contao\Environment; use Contao\StringUtil; @@ -33,14 +36,20 @@ use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\ContaoBackendViewTemplate; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\ContaoWidgetManager; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\TreePicker; +use ContaoCommunityAlliance\DcGeneral\Data\DataProviderInterface; use ContaoCommunityAlliance\DcGeneral\Data\ModelId; use ContaoCommunityAlliance\DcGeneral\Data\PropertyValueBag; -use ContaoCommunityAlliance\DcGeneral\DcGeneral; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\Factory\DcGeneralFactory; +use ContaoCommunityAlliance\Translator\TranslatorInterface; use Symfony\Component\DependencyInjection\ContainerAwareInterface; use Symfony\Component\DependencyInjection\ContainerAwareTrait; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBagInterface; +use Symfony\Component\HttpFoundation\Session\SessionInterface; use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; use Symfony\Component\Routing\Annotation\Route; @@ -55,13 +64,6 @@ class BackendTreeController implements ContainerAwareInterface { use ContainerAwareTrait; - /** - * The DcGeneral Object. - * - * @var DcGeneral - */ - private $itemContainer; - /** * Handles the installation process. * @@ -71,9 +73,7 @@ class BackendTreeController implements ContainerAwareInterface */ public function generalTreeAction() { - $this->container->get('contao.framework')->getAdapter(\Contao\Controller::class)->setStaticUrls(); - - return $this->runBackendTree($this->container->get('request_stack')->getCurrentRequest()); + return $this->runBackendTree($this->initializeAndExtractRequest()); } /** @@ -85,9 +85,7 @@ public function generalTreeAction() */ public function generalTreeToggleAction() { - $this->container->get('contao.framework')->getAdapter(\Contao\Controller::class)->setStaticUrls(); - - return $this->runBackendTreeToggle($this->container->get('request_stack')->getCurrentRequest()); + return $this->runBackendTreeToggle($this->initializeAndExtractRequest()); } /** @@ -99,9 +97,7 @@ public function generalTreeToggleAction() */ public function generalTreeBreadCrumbAction() { - $this->container->get('contao.framework')->getAdapter(\Contao\Controller::class)->setStaticUrls(); - - return $this->runBackendTreeBreadCrumb($this->container->get('request_stack')->getCurrentRequest()); + return $this->runBackendTreeBreadCrumb($this->initializeAndExtractRequest()); } /** @@ -113,9 +109,32 @@ public function generalTreeBreadCrumbAction() */ public function generalTreeUpdateAction() { - $this->container->get('contao.framework')->getAdapter(\Contao\Controller::class)->setStaticUrls(); + return $this->runBackendTreeUpdate($this->initializeAndExtractRequest()); + } + + private function initializeAndExtractRequest(): Request + { + $container = $this->container; + assert($container instanceof ContainerInterface); + + $framework = $container->get('contao.framework'); + assert($framework instanceof ContaoFramework); + + /** + * @psalm-suppress InternalMethod + * @var Controller $controller + */ + $controller = $framework->getAdapter(Controller::class); + /** @psalm-suppress DeprecatedMethod */ + $controller->setStaticUrls(); + + $requestStack = $container->get('request_stack'); + assert($requestStack instanceof RequestStack); + + $currentRequest = $requestStack->getCurrentRequest(); + assert($currentRequest instanceof Request); - return $this->runBackendTreeUpdate($this->container->get('request_stack')->getCurrentRequest()); + return $currentRequest; } /** @@ -131,15 +150,7 @@ public function generalTreeUpdateAction() */ private function runBackendTree(Request $request) { - if (null === ($request->query->get('picker'))) { - throw new \InvalidArgumentException('No picker was given here.'); - } - $picker = $this->container->get('contao.picker.builder')->createFromData($request->query->get('picker')); - - $treeSelector = $this->prepareTreeSelector($picker); - - $sessionBag = $this->container->get('session')->getBag('contao_backend'); - $sessionBag->set($treeSelector->getSearchSessionKey(), $picker->getConfig()->getValue()); + [$value, $treeSelector] = $this->getTemplateData($request); $template = new ContaoBackendViewTemplate('be_main'); $template @@ -153,7 +164,7 @@ private function runBackendTree(Request $request) ->set('addSearch', $treeSelector->searchField) ->set('search', $GLOBALS['TL_LANG']['MSC']['search']) ->set('action', StringUtil::ampersand($request->getUri())) - ->set('value', $sessionBag->get($treeSelector->getSearchSessionKey())) + ->set('value', $value) ->set('manager', $GLOBALS['TL_LANG']['MSC']['treepickerManager'] ?? '') ->set('breadcrumb', $GLOBALS['TL_DCA'][$treeSelector->foreignTable]['list']['sorting']['breadcrumb'] ?? ''); @@ -173,21 +184,13 @@ private function runBackendTree(Request $request) */ private function runBackendTreeBreadCrumb(Request $request) { - if (null === ($request->query->get('picker'))) { - throw new \InvalidArgumentException('No picker was given here.'); - } - $picker = $this->container->get('contao.picker.builder')->createFromData($request->query->get('picker')); - - $treeSelector = $this->prepareTreeSelector($picker); - $treeSelector->generatePopup(); - - $sessionBag = $this->container->get('session')->getBag('contao_backend'); - $sessionBag->set($treeSelector->getSearchSessionKey(), $picker->getConfig()->getValue()); + [, $treeSelector] = $this->getTemplateData($request); $message = ' The bread crumb method isn´ implement yet. '; + $treeSelector->generatePopup(); $template = new ContaoBackendViewTemplate('be_main'); $template ->set('isPopup', true) @@ -215,15 +218,7 @@ private function runBackendTreeBreadCrumb(Request $request) */ private function runBackendTreeToggle(Request $request) { - if (null === ($request->query->get('picker'))) { - throw new \InvalidArgumentException('No picker was given here.'); - } - $picker = $this->container->get('contao.picker.builder')->createFromData($request->query->get('picker')); - - $treeSelector = $this->prepareTreeSelector($picker); - - $sessionBag = $this->container->get('session')->getBag('contao_backend'); - $sessionBag->set($treeSelector->getSearchSessionKey(), $picker->getConfig()->getValue()); + [, $treeSelector] = $this->getTemplateData($request); $buffer = $treeSelector->generateAjax(); @@ -251,67 +246,107 @@ private function runBackendTreeUpdate(Request $request) throw new BadRequestHttpException('This request isn`t from type ajax.'); } - if (null === ($request->query->get('picker'))) { - throw new BadRequestHttpException('No picker was given here.'); - } - $picker = $this->container->get('contao.picker.builder')->createFromData($request->query->get('picker')); + [$value, , $picker] = $this->getTemplateData($request, true); - $treeSelector = $this->prepareTreeSelector($picker); + $modelId = ModelId::fromSerialized($picker->getConfig()->getExtra('modelId')); - $sessionBag = $this->container->get('session')->getBag('contao_backend'); - $sessionBag->set( - $treeSelector->getSearchSessionKey(), - $treeSelector->widgetToValue($request->request->get('value')) - ); + $container = $this->container; + assert($container instanceof ContainerInterface); + $translator = $container->get('cca.translator.contao_translator'); + assert($translator instanceof TranslatorInterface); - $modelId = ModelId::fromSerialized($picker->getConfig()->getExtra('modelId')); + $dispatcher = $container->get('event_dispatcher'); + assert($dispatcher instanceof EventDispatcherInterface); $factory = new DcGeneralFactory(); $general = $factory ->setContainerName($modelId->getDataProviderName()) - ->setTranslator($this->container->get('cca.translator.contao_translator')) - ->setEventDispatcher($this->container->get('event_dispatcher')) + ->setTranslator($translator) + ->setEventDispatcher($dispatcher) ->createDcGeneral(); $dataProvider = $general->getEnvironment()->getDataProvider(); + assert($dataProvider instanceof DataProviderInterface); if (!($model = $dataProvider->fetch($dataProvider->getEmptyConfig()->setId($modelId->getId())))) { $model = $dataProvider->getEmptyModel(); } - $widgetValue = $treeSelector->widgetToValue($request->request->get('value')); - if (\is_array($widgetValue)) { + if (\is_array($value)) { $values = []; - // Clean keys the have empty value. - foreach ($widgetValue as $index => $value) { + // Clean keys they have empty value. + foreach ($value as $index => $val) { if ( - empty($value) + empty($val) // The first key entry has the value on, if the checkbox for all checked. - || ((0 === $index) && ('on' === $value)) + || ((0 === $index) && ('on' === $val)) ) { continue; } - $values[] = $value; + $values[] = $val; } - $widgetValue = $values; + $value = $values; } + $propertyName = $picker->getConfig()->getExtra('propertyName'); $propertyValues = new PropertyValueBag(); - $propertyValues->setPropertyValue($picker->getConfig()->getExtra('propertyName'), $widgetValue); - $general->getEnvironment()->getController()->updateModelFromPropertyBag($model, $propertyValues); + $propertyValues->setPropertyValue($propertyName, $value); + + $controller = $general->getEnvironment()->getController(); + assert($controller instanceof ControllerInterface); + + $controller->updateModelFromPropertyBag($model, $propertyValues); $widgetManager = new ContaoWidgetManager($general->getEnvironment(), $model); - $buffer = - $widgetManager->renderWidget($picker->getConfig()->getExtra('propertyName'), false, $propertyValues); + $buffer = $widgetManager->renderWidget($propertyName, false, $propertyValues); $response = new Response($buffer); - $response->headers->set('Content-Type', 'txt/html; charset=' . Config::get('characterSet')); + $response->headers->set('Content-Type', 'txt/html; charset=' . (Config::get('characterSet') ?? '')); return $response; } + /** + * @param Request $request The request to obtain information from. + * @param bool $valueFromRequest Flag if the value shall be read from the request. + * + * @return array{0: string|list, 1: TreePicker, 2: PickerInterface} + */ + private function getTemplateData(Request $request, bool $valueFromRequest = false): array + { + if ('' === ($getPicker = (string) $request->query->get('picker'))) { + throw new BadRequestHttpException('No picker was given here.'); + } + $container = $this->container; + assert($container instanceof ContainerInterface); + $pickerBuilder = $container->get('contao.picker.builder'); + assert($pickerBuilder instanceof PickerBuilderInterface); + $picker = $pickerBuilder->createFromData($getPicker); + assert($picker instanceof PickerInterface); + $treeSelector = $this->prepareTreeSelector($picker); + $session = $container->get('session'); + assert($session instanceof SessionInterface); + $sessionBag = $session->getBag('contao_backend'); + assert($sessionBag instanceof AttributeBagInterface); + $value = $picker->getConfig()->getValue(); + if ($valueFromRequest) { + if (null !== $reqValue = $request->request->get('value')) { + $reqValue = (string) $reqValue; + } + $value = $treeSelector->widgetToValue($reqValue); + } + + $sessionBag->set($treeSelector->getSearchSessionKey(), $value); + + return [ + $sessionBag->get($treeSelector->getSearchSessionKey()), + $treeSelector, + $picker + ]; + } + /** * Prepare the tree selector. * @@ -335,21 +370,35 @@ private function prepareTreeSelector(PickerInterface $picker) throw new \InvalidArgumentException('The field name contains invalid characters'); } - $sessionBag = $this->container->get('session')->getBag('contao_backend'); + $container = $this->container; + assert($container instanceof ContainerInterface); + + $session = $container->get('session'); + assert($session instanceof SessionInterface); + + $sessionBag = $session->getBag('contao_backend'); + assert($sessionBag instanceof AttributeBagInterface); + // Define the current ID. \define('CURRENT_ID', ($table ? $sessionBag->get('CURRENT_ID') : $modelId->getId())); - $this->itemContainer = (new DcGeneralFactory()) + $translator = $container->get('cca.translator.contao_translator'); + assert($translator instanceof TranslatorInterface); + + $dispatcher = $container->get('event_dispatcher'); + assert($dispatcher instanceof EventDispatcherInterface); + + $itemContainer = (new DcGeneralFactory()) ->setContainerName($modelId->getDataProviderName()) - ->setTranslator($this->container->get('cca.translator.contao_translator')) - ->setEventDispatcher($this->container->get('event_dispatcher')) + ->setTranslator($translator) + ->setEventDispatcher($dispatcher) ->createDcGeneral(); + $definition = $itemContainer->getEnvironment()->getDataDefinition(); + assert($definition instanceof ContainerInterface); + // Merge with the information from the data container. - $property = $this - ->itemContainer - ->getEnvironment() - ->getDataDefinition() + $property = $definition ->getPropertiesDefinition() ->getProperty($picker->getConfig()->getExtra('propertyName')); @@ -359,16 +408,20 @@ private function prepareTreeSelector(PickerInterface $picker) } $information['eval'] = array_merge($property->getExtra(), $information['eval']); - $treeSelector = new $GLOBALS['BE_FFL']['DcGeneralTreePicker']( + $dcCompat = new DcCompat($itemContainer->getEnvironment()); + /** @var class-string $class */ + $class = $GLOBALS['BE_FFL']['DcGeneralTreePicker']; + /** @psalm-suppress UnsafeInstantiation - No other way to instantiate. */ + $treeSelector = new $class( Widget::getAttributesFromDca( $information, $field, \array_filter(\explode(',', $picker->getConfig()->getValue())), $field, $table, - new DcCompat($this->itemContainer->getEnvironment()) + $dcCompat ), - new DcCompat($this->itemContainer->getEnvironment()) + $dcCompat ); $treeSelector->id = 'tl_listing'; diff --git a/src/Controller/ControllerInterface.php b/src/Controller/ControllerInterface.php index d8bf48ec..8b309bf9 100644 --- a/src/Controller/ControllerInterface.php +++ b/src/Controller/ControllerInterface.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -15,7 +15,7 @@ * @author Tristan Lins * @author David Molineus * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -53,7 +53,7 @@ public function setEnvironment(EnvironmentInterface $environment); public function getEnvironment(); /** - * Handle a action within this environment. + * Handle an action within this environment. * * @param Action $action The action to be executed. * @@ -95,8 +95,8 @@ public function searchParentOf(ModelInterface $model); * (or originating table of the model, if no provider name has been given) for all levels and parent child * conditions. * - * @param ModelInterface $objModel The model to assemble children from. - * @param string $strDataProvider The name of the data provider to fetch children from. + * @param ModelInterface $model The model to assemble children from. + * @param string $providerName The name of the data provider to fetch children from. * * @return array * @@ -104,7 +104,7 @@ public function searchParentOf(ModelInterface $model); * * @see \ContaoCommunityAlliance\DcGeneral\Controller\ModelCollector::collectChildrenOf(). */ - public function assembleAllChildrenFrom($objModel, $strDataProvider = ''); + public function assembleAllChildrenFrom($model, $providerName = ''); /** * Update the current model from a post request. Additionally, trigger meta palettes, if installed. @@ -171,9 +171,9 @@ public function getModelFromClipboardItem(ItemInterface $item); /** * Retrieve models from the clipboard items. * - * @param array|ItemInterface[] $items The clipboard items. + * @param list $items The clipboard items. * - * @return CollectionInterface|ModelInterface[] + * @return CollectionInterface */ public function getModelsFromClipboardItems(array $items); @@ -184,21 +184,21 @@ public function getModelsFromClipboardItems(array $items); * * @param ModelIdInterface $parentModelId The optional parent id. If not given, the models must not have a parent. * - * @return CollectionInterface|\ContaoCommunityAlliance\DcGeneral\Data\ModelInterface[] + * @return CollectionInterface */ public function getModelsFromClipboard(ModelIdInterface $parentModelId = null); /** * Evaluate clipboard items, then return the corresponding models. * - * @param ModelIdInterface $source The source model id. - * @param ModelIdInterface $after The previous model id. - * @param ModelIdInterface $into The hierarchical parent model id. - * @param ModelIdInterface $parentModelId The parent model id. - * @param FilterInterface $filter Clipboard filter. - * @param array $items Write-back evaluated clipboard items. + * @param ModelIdInterface|null $source The source model id. + * @param ModelIdInterface|null $after The previous model id. + * @param ModelIdInterface|null $into The hierarchical parent model id. + * @param ModelIdInterface|null $parentModelId The parent model id. + * @param FilterInterface|null $filter Clipboard filter. + * @param array $items Write-back evaluated clipboard items. * - * @return CollectionInterface|ModelInterface[] + * @return CollectionInterface */ public function applyClipboardActions( ModelIdInterface $source = null, @@ -212,9 +212,9 @@ public function applyClipboardActions( /** * Paste the content of the clipboard onto the top. * - * @param CollectionInterface $models The models to be inserted. - * @param string $sortedBy The name of the sorting property. - * @param ModelIdInterface $parentId The parent model ID. + * @param CollectionInterface $models The models to be inserted. + * @param string $sortedBy The name of the sorting property. + * @param ModelIdInterface|null $parentId The parent model ID. * * @return void */ diff --git a/src/Controller/DefaultController.php b/src/Controller/DefaultController.php index b9ed23a1..c89b49ae 100644 --- a/src/Controller/DefaultController.php +++ b/src/Controller/DefaultController.php @@ -33,6 +33,8 @@ namespace ContaoCommunityAlliance\DcGeneral\Controller; use ContaoCommunityAlliance\DcGeneral\Action; +use ContaoCommunityAlliance\DcGeneral\BaseConfigRegistryInterface; +use ContaoCommunityAlliance\DcGeneral\Clipboard\ClipboardInterface; use ContaoCommunityAlliance\DcGeneral\Clipboard\Filter; use ContaoCommunityAlliance\DcGeneral\Clipboard\FilterInterface; use ContaoCommunityAlliance\DcGeneral\Clipboard\Item; @@ -48,6 +50,7 @@ use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; use ContaoCommunityAlliance\DcGeneral\Data\ModelManipulator; use ContaoCommunityAlliance\DcGeneral\Data\MultiLanguageDataProviderInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\BasicDefinitionInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\Properties\PropertyInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\GroupAndSortingInformationInterface; @@ -61,6 +64,8 @@ use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralInvalidArgumentException; use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralRuntimeException; use ContaoCommunityAlliance\DcGeneral\Factory\DcGeneralFactory; +use ContaoCommunityAlliance\Translator\TranslatorInterface; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; /** * This class serves as main controller class in dc general. @@ -73,6 +78,7 @@ * @SuppressWarnings(PHPMD.TooManyFields) * @SuppressWarnings(PHPMD.TooManyMethods) * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * @psalm-suppress MissingConstructor */ class DefaultController implements ControllerInterface { @@ -133,11 +139,17 @@ public function __call($name, $arguments) */ public function setEnvironment(EnvironmentInterface $environment) { - $this->environment = $environment; - $definition = $environment->getDataDefinition(); + $this->environment = $environment; + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $mode = $definition->getBasicDefinition()->getMode(); + assert(\is_int($mode)); + $this->relationshipManager = new RelationshipManager( $definition->getModelRelationshipDefinition(), - $definition->getBasicDefinition()->getMode() + $mode ); $this->modelCollector = new ModelCollector($this->environment); @@ -158,9 +170,13 @@ public function getEnvironment() public function handle(Action $action) { $event = new ActionEvent($this->getEnvironment(), $action); - $this->getEnvironment()->getEventDispatcher()->dispatch($event, DcGeneralEvents::ACTION); - return $event->getResponse(); + $dispatcher = $this->getEnvironment()->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $dispatcher->dispatch($event, DcGeneralEvents::ACTION); + + return (string) $event->getResponse(); } /** @@ -168,7 +184,7 @@ public function handle(Action $action) * * @deprecated Use \ContaoCommunityAlliance\DcGeneral\Controller\ModelCollector::searchParentOfIn(). * - * @see \ContaoCommunityAlliance\DcGeneral\Controller\ModelCollector::searchParentOfIn(). + * @see ModelCollector::searchParentOfIn */ public function searchParentOfIn(ModelInterface $model, CollectionInterface $models) { @@ -179,7 +195,12 @@ public function searchParentOfIn(ModelInterface $model, CollectionInterface $mod ); // @codingStandardsIgnoreEnd - return $this->modelCollector->searchParentOfIn($model, $models); + $parent = $this->modelCollector->searchParentOfIn($model, $models); + if (null === $parent) { + throw new \RuntimeException('Not found'); + } + + return $parent; } /** @@ -189,7 +210,7 @@ public function searchParentOfIn(ModelInterface $model, CollectionInterface $mod * * @deprecated Use \ContaoCommunityAlliance\DcGeneral\Controller\ModelCollector::searchParentOf(). * - * @see \ContaoCommunityAlliance\DcGeneral\Controller\ModelCollector::searchParentOf(). + * @see ModelCollector::searchParentOf */ public function searchParentOf(ModelInterface $model) { @@ -200,7 +221,12 @@ public function searchParentOf(ModelInterface $model) ); // @codingStandardsIgnoreEnd - return $this->modelCollector->searchParentOf($model); + $parent = $this->modelCollector->searchParentOf($model); + if (null === $parent) { + throw new \RuntimeException('Not found'); + } + + return $parent; } /** @@ -208,7 +234,7 @@ public function searchParentOf(ModelInterface $model) * * @deprecated Use \ContaoCommunityAlliance\DcGeneral\Controller\ModelCollector::collectChildrenOf(). * - * @see \ContaoCommunityAlliance\DcGeneral\Controller\ModelCollector::collectChildrenOf(). + * @see ModelCollector::collectChildrenOf */ public function assembleAllChildrenFrom($model, $providerName = '') { @@ -225,17 +251,15 @@ public function assembleAllChildrenFrom($model, $providerName = '') /** * Retrieve all siblings of a given model. * - * @param ModelInterface $model The model for which the siblings shall be retrieved from. - * @param string|null $sortingProperty The property name to use for sorting. - * @param ModelIdInterface $parentId The (optional) parent id to use. + * @param ModelInterface $model The model for which the siblings shall be retrieved from. + * @param null $sortingProperty The property name to use for sorting. + * @param ModelIdInterface|null $parentId The (optional) parent id to use. * * @return CollectionInterface * - * @throws DcGeneralRuntimeException When no parent model can be located. - * * @deprecated Use ContaoCommunityAlliance\DcGeneral\Controller\ModelCollector::collectSiblingsOf(). * - * @see \ContaoCommunityAlliance\DcGeneral\Controller\ModelCollector::collectSiblingsOf(). + * @see ModelCollector::collectSiblingsOf */ protected function assembleSiblingsFor( ModelInterface $model, @@ -260,15 +284,23 @@ protected function assembleSiblingsFor( * * @return CollectionInterface * - * @throws DcGeneralRuntimeException Unable to retrieve children in non hierarchical mode. + * @throws DcGeneralRuntimeException Unable to retrieve children in non-hierarchical mode. * @throws DcGeneralInvalidArgumentException Invalid configuration. Child condition must be defined. */ protected function assembleChildrenFor(ModelInterface $model, $sortingProperty = null) { - $environment = $this->getEnvironment(); - $definition = $environment->getDataDefinition(); - $provider = $environment->getDataProvider($model->getProviderName()); - $config = $environment->getBaseConfigRegistry()->getBaseConfig(); + $environment = $this->getEnvironment(); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $provider = $environment->getDataProvider($model->getProviderName()); + assert($provider instanceof DataProviderInterface); + + $registry = $environment->getBaseConfigRegistry(); + assert($registry instanceof BaseConfigRegistryInterface); + + $config = $registry->getBaseConfig(); $relationships = $definition->getModelRelationshipDefinition(); if (BasicDefinitionInterface::MODE_HIERARCHICAL !== $definition->getBasicDefinition()->getMode()) { @@ -284,10 +316,12 @@ protected function assembleChildrenFor(ModelInterface $model, $sortingProperty = $config->setFilter($condition->getFilter($model)); if ($sortingProperty) { - $config->setSorting([(string) $sortingProperty => 'ASC']); + $config->setSorting([$sortingProperty => 'ASC']); } - return $provider->fetchAll($config); + $childrenCollection = $provider->fetchAll($config); + assert($childrenCollection instanceof CollectionInterface); + return $childrenCollection; } /** @@ -295,11 +329,12 @@ protected function assembleChildrenFor(ModelInterface $model, $sortingProperty = */ public function updateModelFromPropertyBag($model, $propertyValues) { - if (!$propertyValues) { - return $this; - } $environment = $this->getEnvironment(); - $properties = $environment->getDataDefinition()->getPropertiesDefinition(); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $properties = $definition->getPropertiesDefinition(); ModelManipulator::updateModelFromPropertyBag($properties, $model, $propertyValues); @@ -309,18 +344,18 @@ public function updateModelFromPropertyBag($model, $propertyValues) /** * Return all supported languages from the default data data provider. * - * @param mixed $currentID The id of the item for which to retrieve the valid languages. + * @param mixed $mixID The id of the item for which to retrieve the valid languages. * * @return array */ - public function getSupportedLanguages($currentID) + public function getSupportedLanguages($mixID) { $environment = $this->getEnvironment(); $dataProvider = $environment->getDataProvider(); // Check if current data provider supports multi language. if ($dataProvider instanceof MultiLanguageDataProviderInterface) { - $supportedLanguages = $dataProvider->getLanguages($currentID); + $supportedLanguages = $dataProvider->getLanguages($mixID); } else { $supportedLanguages = null; } @@ -331,6 +366,7 @@ public function getSupportedLanguages($currentID) } $translator = $environment->getTranslator(); + assert($translator instanceof TranslatorInterface); // Make an array from the collection. $languages = []; @@ -388,9 +424,14 @@ public function createClonedModel($model) $clone = clone $model; $clone->setId(null); - $environment = $this->getEnvironment(); - $properties = $environment->getDataDefinition()->getPropertiesDefinition(); + $environment = $this->getEnvironment(); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $properties = $definition->getPropertiesDefinition(); $dataProvider = $environment->getDataProvider($clone->getProviderName()); + assert($dataProvider instanceof DataProviderInterface); foreach (\array_keys($clone->getPropertiesAsArray()) as $propName) { // If the property is not known, remove it. @@ -412,7 +453,7 @@ public function createClonedModel($model) * * @deprecated Use \ContaoCommunityAlliance\DcGeneral\Controller\ModelCollector::getModel(). * - * @see \ContaoCommunityAlliance\DcGeneral\Controller\ModelCollector::getModel(). + * @see ModelCollector::getModel */ public function fetchModelFromProvider($modelId, $providerName = null) { @@ -423,7 +464,12 @@ public function fetchModelFromProvider($modelId, $providerName = null) ); // @codingStandardsIgnoreEnd - return $this->modelCollector->getModel($modelId, $providerName); + $model = $this->modelCollector->getModel($modelId, $providerName); + if (null === $model) { + throw new \RuntimeException('Not found'); + } + + return $model; } /** @@ -431,9 +477,14 @@ public function fetchModelFromProvider($modelId, $providerName = null) */ public function createEmptyModelWithDefaults() { - $environment = $this->getEnvironment(); - $definition = $environment->getDataDefinition(); - $dataProvider = $environment->getDataProvider(); + $environment = $this->getEnvironment(); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $dataProvider = $environment->getDataProvider(); + assert($dataProvider instanceof DataProviderInterface); + $propertyDefinition = $definition->getPropertiesDefinition(); $properties = $propertyDefinition->getProperties(); $model = $dataProvider->getEmptyModel(); @@ -481,7 +532,10 @@ public function getModelsFromClipboardItems(array $items) continue; } - $models->push($environment->getDataProvider($item->getDataProviderName())->getEmptyModel()); + $dataProvider = $environment->getDataProvider($item->getDataProviderName()); + assert($dataProvider instanceof DataProviderInterface); + + $models->push($dataProvider->getEmptyModel()); } return $models; @@ -492,11 +546,17 @@ public function getModelsFromClipboardItems(array $items) */ public function getModelsFromClipboard(ModelIdInterface $parentModelId = null) { - $environment = $this->getEnvironment(); - $dataDefinition = $environment->getDataDefinition(); + $environment = $this->getEnvironment(); + + $dataDefinition = $environment->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + $basicDefinition = $dataDefinition->getBasicDefinition(); $modelProviderName = $basicDefinition->getDataProvider(); - $clipboard = $environment->getClipboard(); + assert(\is_string($modelProviderName)); + + $clipboard = $environment->getClipboard(); + assert($clipboard instanceof ClipboardInterface); $filter = new Filter(); $filter->andModelIsFromProvider($modelProviderName); @@ -541,67 +601,87 @@ public function applyClipboardActions( */ private function getActionsFromSource(ModelIdInterface $source, ModelIdInterface $parentModelId = null) { - $basicDefinition = $this->getEnvironment()->getDataDefinition()->getBasicDefinition(); + $definition = $this->getEnvironment()->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $basicDefinition = $definition->getBasicDefinition(); + assert($basicDefinition instanceof BasicDefinitionInterface); + + $dataProvider = $basicDefinition->getDataProvider(); + assert(\is_string($dataProvider)); $filter = new Filter(); - $filter->andModelIsFromProvider($basicDefinition->getDataProvider()); + $filter->andModelIsFromProvider($dataProvider); if ($basicDefinition->getParentDataProvider()) { - $filter->andParentIsFromProvider($basicDefinition->getParentDataProvider()); + $parentDataProvider = $basicDefinition->getDataProvider(); + assert(\is_string($parentDataProvider)); + + $filter->andParentIsFromProvider($parentDataProvider); } else { $filter->andHasNoParent(); } $filter->andModelIs($source); - $item = $this->getEnvironment()->getClipboard()->fetch($filter)[0] ?? null; + $clipboard = $this->getEnvironment()->getClipboard(); + assert($clipboard instanceof ClipboardInterface); - $action = $item ? $item->getAction() : ItemInterface::CUT; - $model = $this->modelCollector->getModel($source); - $actions = [ + $item = $clipboard->fetch($filter)[0] ?? null; + + $action = $item ? $item->getAction() : ItemInterface::CUT; + $model = $this->modelCollector->getModel($source); + assert($model instanceof ModelInterface); + + return [ [ 'model' => $model, 'item' => new Item($action, $parentModelId, ModelId::fromModel($model)) ] ]; - - return $actions; } /** * Fetch actions from the clipboard. * - * @param FilterInterface|null $filter The clipboard filter. - * @param ModelIdInterface $parentModelId The parent id. + * @param FilterInterface|null $filter The clipboard filter. + * @param ModelIdInterface|null $parentModelId The parent id. * * @return array */ private function fetchModelsFromClipboard(FilterInterface $filter = null, ModelIdInterface $parentModelId = null) { - $environment = $this->getEnvironment(); + $environment = $this->getEnvironment(); + assert($environment instanceof EnvironmentInterface); + $dataDefinition = $environment->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); if (!$filter) { $filter = new Filter(); } - $basicDefinition = $dataDefinition->getBasicDefinition(); - $modelProviderName = $basicDefinition->getDataProvider(); - $filter->andModelIsFromProvider($modelProviderName); - if ($parentModelId) { - $filter->andParentIsFromProvider($parentModelId->getDataProviderName()); - } else { - $filter->andHasNoParent(); + if ($filter instanceof Filter) { + $basicDefinition = $dataDefinition->getBasicDefinition(); + $modelProviderName = $basicDefinition->getDataProvider(); + assert(\is_string($modelProviderName)); + $filter->andModelIsFromProvider($modelProviderName); + if ($parentModelId) { + $filter->andParentIsFromProvider($parentModelId->getDataProviderName()); + } else { + $filter->andHasNoParent(); + } } - $environment = $this->getEnvironment(); - $clipboard = $environment->getClipboard(); - $items = $clipboard->fetch($filter); - $actions = []; + $clipboard = $environment->getClipboard(); + assert($clipboard instanceof ClipboardInterface); + + $items = $clipboard->fetch($filter); + $actions = []; foreach ($items as $item) { $model = null; - if (!$item->isCreate() && $item->getModelId()) { - $model = $this->modelCollector->getModel($item->getModelId()->getId(), $item->getDataProviderName()); + if (!$item->isCreate() && null !== ($model = $item->getModelId())) { + $model = $this->modelCollector->getModel((string) $model->getId(), $item->getDataProviderName()); } $actions[] = [ @@ -616,11 +696,11 @@ private function fetchModelsFromClipboard(FilterInterface $filter = null, ModelI /** * Effectively do the actions. * - * @param array $actions The actions collection. - * @param ModelIdInterface $after The previous model id. - * @param ModelIdInterface $into The hierarchical parent model id. - * @param ModelIdInterface $parentModelId The parent model id. - * @param array $items Write-back clipboard items. + * @param array $actions The action's collection. + * @param ModelIdInterface|null $after The previous model id. + * @param ModelIdInterface|null $into The hierarchical parent model id. + * @param ModelIdInterface|null $parentModelId The parent model id. + * @param array $items Write-back clipboard items. * * @return CollectionInterface */ @@ -637,22 +717,22 @@ private function doActions( $parentModel = null; } - // Holds models, that need deep-copy + // Holds models, that need deep-copy. $deepCopyList = []; - // Apply create and copy actions + // Apply to create and copy actions. foreach ($actions as &$action) { $this->applyAction($action, $deepCopyList, $parentModel); } unset($action); - // When pasting after another model, apply same grouping information + // When pasting after another model, apply same grouping information. $this->ensureSameGrouping($actions, $after); - // Now apply sorting and persist all models + // Now apply sorting and persist all models. $models = $this->sortAndPersistModels($actions, $after, $into, $parentModelId, $items); - // At least, go ahead with the deep copy + // At least, go ahead with the deep copy. $this->doDeepCopy($deepCopyList); return $models; @@ -663,13 +743,12 @@ private function doActions( * * This will create or clone the model in the action. * - * @param array $action The action, containing a model and an item. - * @param array $deepCopyList A list of models that need deep copy. - * @param ModelInterface $parentModel The parent model. + * @param array $action The action, containing a model and an item. + * @param array $deepCopyList A list of models that need deep copy. + * @param ModelInterface|null $parentModel The parent model. * * @return void * - * @throws \UnexpectedValueException When the action is neither create, copy or deep copy. */ private function applyAction(array &$action, array &$deepCopyList, ModelInterface $parentModel = null) { @@ -683,8 +762,10 @@ private function applyAction(array &$action, array &$deepCopyList, ModelInterfac // create new model $model = $this->createEmptyModelWithDefaults(); } elseif ($item->isCopy() || $isDeepCopy = $item->isDeepCopy()) { + assert($model instanceof ModelInterface); // copy model - $model = $this->modelCollector->getModel(ModelId::fromModel($model)); + $model = $this->modelCollector->getModel(ModelId::fromModel($model)); + assert($model instanceof ModelInterface); $clonedModel = $this->doCloneAction($model); if ($isDeepCopy) { @@ -721,16 +802,19 @@ private function doCloneAction(ModelInterface $model) { $environment = $this->getEnvironment(); + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + // Make a duplicate. $clonedModel = $this->createClonedModel($model); // Trigger the pre duplicate event. $duplicateEvent = new PreDuplicateModelEvent($environment, $clonedModel, $model); - $environment->getEventDispatcher()->dispatch($duplicateEvent, $duplicateEvent::NAME); + $dispatcher->dispatch($duplicateEvent, $duplicateEvent::NAME); // And trigger the post event for it. $duplicateEvent = new PostDuplicateModelEvent($environment, $clonedModel, $model); - $environment->getEventDispatcher()->dispatch($duplicateEvent, $duplicateEvent::NAME); + $dispatcher->dispatch($duplicateEvent, $duplicateEvent::NAME); return $clonedModel; } @@ -738,8 +822,8 @@ private function doCloneAction(ModelInterface $model) /** * Ensure all models have the same grouping. * - * @param array $actions The actions collection. - * @param ModelIdInterface $after The previous model id. + * @param array $actions The action's collection. + * @param ModelIdInterface|null $after The previous model id. * * @return void */ @@ -751,6 +835,8 @@ private function ensureSameGrouping(array $actions, ModelIdInterface $after = nu // when pasting after another item, inherit the grouping field $groupingField = $groupingMode['property']; $previous = $this->modelCollector->getModel($after); + assert($previous instanceof ModelInterface); + $groupingValue = $previous->getProperty($groupingField); foreach ($actions as $action) { @@ -764,15 +850,14 @@ private function ensureSameGrouping(array $actions, ModelIdInterface $after = nu /** * Apply sorting and persist all models. * - * @param array $actions The actions collection. - * @param ModelIdInterface $after The previous model id. - * @param ModelIdInterface $into The hierarchical parent model id. - * @param ModelIdInterface $parentModelId The parent model id. - * @param array $items Write-back clipboard items. + * @param array $actions The actions collection. + * @param ModelIdInterface|null $after The previous model id. + * @param ModelIdInterface|null $into The hierarchical parent model id. + * @param ModelIdInterface|null $parentModelId The parent model id. + * @param array $items Write-back clipboard items. * - * @return DefaultCollection|ModelInterface[] + * @return DefaultCollection * - * @throws DcGeneralRuntimeException When the parameters for the pasting destination are invalid. */ private function sortAndPersistModels( array $actions, @@ -781,8 +866,8 @@ private function sortAndPersistModels( ModelIdInterface $parentModelId = null, array &$items = [] ) { - /** @var DefaultCollection|ModelInterface[] $models */ $models = $this->createModelCollectionFromActions($actions, $items); + assert($models instanceof CollectionInterface); $this->triggerPrePasteModel($models); @@ -801,8 +886,8 @@ private function sortAndPersistModels( /** * Process paste the collection of models after the a model. * - * @param CollectionInterface $models The collection of models. - * @param ModelIdInterface $after The paste after model. + * @param CollectionInterface $models The collection of models. + * @param ModelIdInterface|null $after The paste after model. * * @return void */ @@ -810,8 +895,12 @@ private function processPasteAfter(CollectionInterface $models, ModelIdInterface { if ($after && $models->count() && $after->getId()) { $manualSorting = ViewHelpers::getManualSortingProperty($this->getEnvironment()); + assert(\is_string($manualSorting)); + + $model = $this->modelCollector->getModel($after); + assert($model instanceof ModelInterface); - $this->pasteAfter($this->modelCollector->getModel($after), $models, $manualSorting); + $this->pasteAfter($model, $models, $manualSorting); $this->triggerPostPasteModel($models); $this->clearModelCollection($models); @@ -819,10 +908,10 @@ private function processPasteAfter(CollectionInterface $models, ModelIdInterface } /** - * Process paste the collection of models into the a model. + * Process paste the collection of models into the model. * - * @param CollectionInterface $models The collection of models. - * @param ModelIdInterface $into The paste into model. + * @param CollectionInterface $models The collection of models. + * @param ModelIdInterface|null $into The paste into model. * * @return void */ @@ -830,8 +919,12 @@ private function processPasteInto(CollectionInterface $models, ModelIdInterface { if ($into && $models->count() && $into->getId()) { $manualSorting = ViewHelpers::getManualSortingProperty($this->getEnvironment()); + assert(\is_string($manualSorting)); + + $model = $this->modelCollector->getModel($into); + assert($model instanceof ModelInterface); - $this->pasteInto($this->modelCollector->getModel($into), $models, $manualSorting); + $this->pasteInto($model, $models, $manualSorting); $this->triggerPostPasteModel($models); $this->clearModelCollection($models); @@ -841,10 +934,10 @@ private function processPasteInto(CollectionInterface $models, ModelIdInterface /** * Process paste the content of the clipboard onto the top after a model without reference. * - * @param CollectionInterface $models The collection of models. - * @param ModelIdInterface $after The previous model id. - * @param ModelIdInterface $into The hierarchical parent model id. - * @param ModelIdInterface $parent The parent model id. + * @param CollectionInterface $models The collection of models. + * @param ModelIdInterface|null $after The previous model id. + * @param ModelIdInterface|null $into The hierarchical parent model id. + * @param ModelIdInterface|null $parent The parent model id. * * @return void */ @@ -859,8 +952,11 @@ private function processPasteTopWithoutReference( && (($after && (0 === (int) $after->getId())) || ($into && (0 === (int) $into->getId()))) ) { - $manualSorting = ViewHelpers::getManualSortingProperty($this->getEnvironment()); + $manualSorting = ViewHelpers::getManualSortingProperty($this->getEnvironment()); + assert(\is_string($manualSorting)); + $dataDefinition = $this->getEnvironment()->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); if (BasicDefinitionInterface::MODE_HIERARCHICAL === $dataDefinition->getBasicDefinition()->getMode()) { $this->relationshipManager->setAllRoot($models); @@ -893,6 +989,8 @@ private function processPasteTopAfterModel(CollectionInterface $models, ModelIdI } $dataProvider = $this->getEnvironment()->getDataProvider(); + assert($dataProvider instanceof DataProviderInterface); + $dataProvider->saveEach($models); $this->triggerPostPasteModel($models); @@ -930,7 +1028,11 @@ private function triggerPrePasteModel(CollectionInterface $collection) { foreach ($collection as $model) { $event = new PrePasteModelEvent($this->getEnvironment(), $model); - $this->getEnvironment()->getEventDispatcher()->dispatch($event, $event::NAME); + + $dispatcher = $this->getEnvironment()->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $dispatcher->dispatch($event, $event::NAME); } } @@ -945,7 +1047,11 @@ private function triggerPostPasteModel(CollectionInterface $collection) { foreach ($collection as $model) { $event = new PostPasteModelEvent($this->getEnvironment(), $model); - $this->getEnvironment()->getEventDispatcher()->dispatch($event, $event::NAME); + + $dispatcher = $this->getEnvironment()->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $dispatcher->dispatch($event, $event::NAME); } } @@ -964,8 +1070,11 @@ protected function doDeepCopy(array $deepCopyList) return; } - $factory = DcGeneralFactory::deriveFromEnvironment($this->getEnvironment()); - $dataDefinition = $this->getEnvironment()->getDataDefinition(); + $factory = DcGeneralFactory::deriveFromEnvironment($this->getEnvironment()); + + $dataDefinition = $this->getEnvironment()->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + $modelRelationshipDefinition = $dataDefinition->getModelRelationshipDefinition(); $childConditions = $modelRelationshipDefinition->getChildConditions($dataDefinition->getName()); @@ -981,13 +1090,22 @@ protected function doDeepCopy(array $deepCopyList) // create new destination environment $destinationName = $childCondition->getDestinationName(); $factory->setContainerName($destinationName); - $destinationEnvironment = $factory->createEnvironment(); + $destinationEnvironment = $factory->createEnvironment(); + $destinationDataDefinition = $destinationEnvironment->getDataDefinition(); + assert($destinationDataDefinition instanceof ContainerInterface); + $destinationViewDefinition = $destinationDataDefinition->getDefinition( Contao2BackendViewDefinitionInterface::NAME ); - $destinationDataProvider = $destinationEnvironment->getDataProvider(); - $destinationController = $destinationEnvironment->getController(); + assert($destinationViewDefinition instanceof Contao2BackendViewDefinitionInterface); + + $destinationDataProvider = $destinationEnvironment->getDataProvider(); + assert($destinationDataProvider instanceof DataProviderInterface); + + $destinationController = $destinationEnvironment->getController(); + assert($destinationController instanceof ControllerInterface); + /** @var Contao2BackendViewDefinitionInterface $destinationViewDefinition */ /** @var DefaultController $destinationController */ $listingConfig = $destinationViewDefinition->getListingConfig(); @@ -1017,6 +1135,10 @@ protected function doDeepCopy(array $deepCopyList) // build the copy actions foreach ($children as $childModel) { + if (!($childModel instanceof ModelInterface)) { + continue; + } + $childModelId = ModelId::fromModel($childModel); $actions[] = [ @@ -1044,11 +1166,18 @@ public function pasteTop(CollectionInterface $models, $sortedBy, ModelIdInterfac { $environment = $this->getEnvironment(); + $firstModel = $models->get(0); + assert($firstModel instanceof ModelInterface); + // Enforce proper sorting now. - $siblings = $this->modelCollector->collectSiblingsOf($models->get(0), $sortedBy, $parentId); + $siblings = $this->modelCollector->collectSiblingsOf($firstModel, $sortedBy, $parentId); $newList = (new SortingManager($models, $siblings, $sortedBy, null))->getResults(); + assert($newList instanceof CollectionInterface); + + $dataProvider = $environment->getDataProvider($firstModel->getProviderName()); + assert($dataProvider instanceof DataProviderInterface); - $environment->getDataProvider($models->get(0)->getProviderName())->saveEach($newList); + $dataProvider->saveEach($newList); } /** @@ -1061,11 +1190,15 @@ public function pasteAfter(ModelInterface $previousModel, CollectionInterface $m if (0 === $models->length()) { throw new \RuntimeException('No models passed to pasteAfter().'); } + $environment = $this->getEnvironment(); + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + if ( \in_array( - $environment->getDataDefinition()->getBasicDefinition()->getMode(), + $definition->getBasicDefinition()->getMode(), [ BasicDefinitionInterface::MODE_HIERARCHICAL, BasicDefinitionInterface::MODE_PARENTEDLIST @@ -1074,6 +1207,8 @@ public function pasteAfter(ModelInterface $previousModel, CollectionInterface $m ) { if (!$this->relationshipManager->isRoot($previousModel)) { $parentModel = $this->modelCollector->searchParentOf($previousModel); + assert($parentModel instanceof ModelInterface); + $parentName = $parentModel->getProviderName(); $this->relationshipManager->setSameParentForAll($models, $previousModel, $parentName); } else { @@ -1084,8 +1219,12 @@ public function pasteAfter(ModelInterface $previousModel, CollectionInterface $m // Enforce proper sorting now. $siblings = $this->modelCollector->collectSiblingsOf($previousModel, $sortedBy); $newList = (new SortingManager($models, $siblings, $sortedBy, $previousModel))->getResults(); + assert($newList instanceof CollectionInterface); + + $dataProvider = $environment->getDataProvider($previousModel->getProviderName()); + assert($dataProvider instanceof DataProviderInterface); - $environment->getDataProvider($previousModel->getProviderName())->saveEach($newList); + $dataProvider->saveEach($newList); } /** @@ -1100,8 +1239,15 @@ public function pasteInto(ModelInterface $parentModel, CollectionInterface $mode // Enforce proper sorting now. $siblings = $this->assembleChildrenFor($parentModel, $sortedBy); $newList = (new SortingManager($models, $siblings, $sortedBy))->getResults(); + assert($newList instanceof CollectionInterface); + + $firstItem = $newList->get(0); + assert($firstItem instanceof ModelInterface); + + $dataProvider = $environment->getDataProvider($firstItem->getProviderName()); + assert($dataProvider instanceof DataProviderInterface); - $environment->getDataProvider($newList->get(0)->getProviderName())->saveEach($newList); + $dataProvider->saveEach($newList); } /** diff --git a/src/Controller/ModelCollector.php b/src/Controller/ModelCollector.php index 5babbb76..99c4b25a 100644 --- a/src/Controller/ModelCollector.php +++ b/src/Controller/ModelCollector.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2022 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -16,13 +16,14 @@ * @author Ingolf Steinhardt * @author David Molineus * @author Stefan Heimes - * @copyright 2013-2022 Contao Community Alliance. + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ namespace ContaoCommunityAlliance\DcGeneral\Controller; +use ContaoCommunityAlliance\DcGeneral\BaseConfigRegistryInterface; use ContaoCommunityAlliance\DcGeneral\Contao\DataDefinition\Definition\Contao2BackendViewDefinitionInterface; use ContaoCommunityAlliance\DcGeneral\Data\CollectionInterface; use ContaoCommunityAlliance\DcGeneral\Data\ConfigInterface; @@ -30,6 +31,7 @@ use ContaoCommunityAlliance\DcGeneral\Data\ModelId; use ContaoCommunityAlliance\DcGeneral\Data\ModelIdInterface; use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\BasicDefinitionInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\ModelRelationshipDefinitionInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\ModelRelationship\RootConditionInterface; @@ -55,7 +57,7 @@ class ModelCollector /** * The mode the definition is in. * - * @var int + * @var int|null */ private $definitionMode; @@ -76,35 +78,35 @@ class ModelCollector /** * The root data provider. * - * @var DataProviderInterface + * @var DataProviderInterface|null */ private $rootProvider; /** * The root data provider name. * - * @var string + * @var string|null */ private $rootProviderName; /** * The parent data provider. * - * @var DataProviderInterface + * @var DataProviderInterface|null */ private $parentProvider; /** * The parent data provider name. * - * @var string + * @var string|null */ private $parentProviderName; /** * The default data provider name. * - * @var string + * @var string|null */ private $defaultProviderName; @@ -117,9 +119,14 @@ class ModelCollector */ public function __construct(EnvironmentInterface $environment) { - $this->environment = $environment; - $definition = $this->environment->getDataDefinition(); - $basicDefinition = $definition->getBasicDefinition(); + $this->environment = $environment; + + $definition = $this->environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $basicDefinition = $definition->getBasicDefinition(); + assert($basicDefinition instanceof BasicDefinitionInterface); + $this->definitionMode = $basicDefinition->getMode(); $this->relationships = $definition->getModelRelationshipDefinition(); $this->defaultProviderName = $basicDefinition->getDataProvider(); @@ -147,20 +154,20 @@ public function __construct(EnvironmentInterface $environment) /** * Fetch a certain model from its provider. * - * @param string|ModelIdInterface $modelId This is either the id of the model or a serialized id. + * @param ModelIdInterface|string $modelId This is either the id of the model or a serialized id. * @param string|null $providerName The name of the provider, if this is empty, the id will be * deserialized and the provider name will get extracted from there. * - * @return ModelInterface + * @return ModelInterface|null * * @throws \InvalidArgumentException When the model id is invalid. */ public function getModel($modelId, $providerName = null) { if (\is_string($modelId)) { - try { + if (null !== $providerName) { $modelId = ModelId::fromValues($providerName, $modelId); - } catch (\Exception $swallow) { + } else { $modelId = ModelId::fromSerialized($modelId); } } @@ -169,14 +176,19 @@ public function getModel($modelId, $providerName = null) throw new \InvalidArgumentException('Invalid model id passed: ' . \var_export($modelId, true)); } - $definition = $this->environment->getDataDefinition(); + $definition = $this->environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + $parentDefinition = $this->environment->getParentDataDefinition(); - $dataProvider = $this->environment->getDataProvider($modelId->getDataProviderName()); - $config = $dataProvider->getEmptyConfig(); + + $dataProvider = $this->environment->getDataProvider($modelId->getDataProviderName()); + assert($dataProvider instanceof DataProviderInterface); + + $config = $dataProvider->getEmptyConfig(); if ($definition->getName() === $modelId->getDataProviderName()) { $propertyDefinition = $definition->getPropertiesDefinition(); - } elseif ($parentDefinition->getName() === $modelId->getDataProviderName()) { + } elseif ($parentDefinition && $parentDefinition->getName() === $modelId->getDataProviderName()) { $propertyDefinition = $parentDefinition->getPropertiesDefinition(); } else { throw new \InvalidArgumentException('Invalid provider name ' . $modelId->getDataProviderName()); @@ -228,8 +240,12 @@ public function searchParentOfIn(ModelInterface $model, CollectionInterface $mod } $provider = $this->environment->getDataProvider($condition->getDestinationName()); - $config = $provider->getEmptyConfig()->setFilter($condition->getFilter($candidate)); - $result = $this->searchParentOfIn($model, $provider->fetchAll($config)); + assert($provider instanceof DataProviderInterface); + + $config = $provider->getEmptyConfig()->setFilter($condition->getFilter($candidate)); + $parentCollection = $provider->fetchAll($config); + assert($parentCollection instanceof CollectionInterface); + $result = $this->searchParentOfIn($model, $parentCollection); if (null !== $result) { return $result; } @@ -289,12 +305,18 @@ public function searchParentFromHierarchical(ModelInterface $model): ?ModelInter if ($this->rootProviderName !== $model->getProviderName()) { throw new DcGeneralInvalidArgumentException( 'Model originates from ' . $model->getProviderName() . - ' but is expected to be from ' . $this->rootProviderName . + ' but is expected to be from ' . ($this->rootProviderName ?? '') . ' can not determine parent.' ); } - $condition = $this->relationships->getChildCondition($this->parentProviderName, $this->rootProviderName); + $parentProviderName = $this->parentProviderName; + assert(\is_string($parentProviderName)); + + $rootProviderName = $this->rootProviderName; + assert(\is_string($rootProviderName)); + + $condition = $this->relationships->getChildCondition($parentProviderName, $rootProviderName); if (null === $condition) { throw new DcGeneralInvalidArgumentException( 'Invalid configuration. Child condition must be defined!' @@ -304,7 +326,9 @@ public function searchParentFromHierarchical(ModelInterface $model): ?ModelInter return $this->parentProvider->fetch($this->parentProvider->getEmptyConfig()->setFilter($inverseFilter)); } - foreach ($this->parentProvider->fetchAll($this->parentProvider->getEmptyConfig()) as $candidate) { + $parentCollection = $this->parentProvider->fetchAll($this->parentProvider->getEmptyConfig()); + assert($parentCollection instanceof CollectionInterface); + foreach ($parentCollection as $candidate) { if ($condition->matches($candidate, $model)) { return $candidate; } @@ -316,39 +340,46 @@ public function searchParentFromHierarchical(ModelInterface $model): ?ModelInter /** * Retrieve all siblings of a given model. * - * @param ModelInterface $model The model for which the siblings shall be retrieved from. - * @param string|null $sortingProperty The property name to use for sorting. - * @param ModelIdInterface $parentId The (optional) parent id to use. + * @param ModelInterface $model The model for which the siblings shall be retrieved from. + * @param string|null $sortingProperty The property name to use for sorting. + * @param ModelIdInterface|null $parentId The (optional) parent id to use. * * @return CollectionInterface * - * @throws DcGeneralRuntimeException When no parent model can be located. */ public function collectSiblingsOf( ModelInterface $model, $sortingProperty = null, ModelIdInterface $parentId = null ) { - $config = $this->environment->getBaseConfigRegistry()->getBaseConfig($parentId); + $registry = $this->environment->getBaseConfigRegistry(); + assert($registry instanceof BaseConfigRegistryInterface); + + $config = $registry->getBaseConfig($parentId); // Add the parent filter. $this->addParentFilter($model, $config); if (null !== $sortingProperty) { - $config->setSorting([(string) $sortingProperty => 'ASC']); + $config->setSorting([$sortingProperty => 'ASC']); } // Handle grouping. $definition = $this->environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + /** @var Contao2BackendViewDefinitionInterface $viewDefinition */ $viewDefinition = $definition->getDefinition(Contao2BackendViewDefinitionInterface::NAME); - if ($viewDefinition && ($viewDefinition instanceof Contao2BackendViewDefinitionInterface)) { + if ($viewDefinition instanceof Contao2BackendViewDefinitionInterface) { $listingConfig = $viewDefinition->getListingConfig(); - $sortingProperties = \array_keys((array) $listingConfig->getDefaultSortingFields()); + /** @psalm-suppress DeprecatedMethod */ + $sortingProperties = \array_keys($listingConfig->getDefaultSortingFields()); $sortingPropertyIndex = \array_search($sortingProperty, $sortingProperties); if (false !== $sortingPropertyIndex && $sortingPropertyIndex > 0) { $sortingProperties = \array_slice($sortingProperties, 0, $sortingPropertyIndex); - $filters = $config->getFilter(); + + $filters = $config->getFilter(); + assert(\is_array($filters)); foreach ($sortingProperties as $propertyName) { $filters[] = [ @@ -362,7 +393,12 @@ public function collectSiblingsOf( } } - return $this->environment->getDataProvider($model->getProviderName())->fetchAll($config); + $dataProvider = $this->environment->getDataProvider($model->getProviderName()); + assert($dataProvider instanceof DataProviderInterface); + + $siblingCollection = $dataProvider->fetchAll($config); + assert($siblingCollection instanceof CollectionInterface); + return $siblingCollection; } /** @@ -406,12 +442,15 @@ public function collectDirectChildrenOf(ModelInterface $model, $providerName = ' * @param string $providerName The name of the data provider to fetch children from. * @param bool $recursive Determine for recursive sampling. For models with included child models. * - * @return array + * @return list * * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ - private function internalCollectChildrenOf(ModelInterface $model, $providerName = '', $recursive = false) - { + private function internalCollectChildrenOf( + ModelInterface $model, + string $providerName = '', + bool $recursive = false + ) { if ('' === $providerName) { $providerName = $model->getProviderName(); } @@ -422,10 +461,13 @@ private function internalCollectChildrenOf(ModelInterface $model, $providerName $childIds = []; foreach ($this->relationships->getChildConditions($model->getProviderName()) as $condition) { $provider = $this->environment->getDataProvider($condition->getDestinationName()); - $config = $provider->getEmptyConfig(); + assert($provider instanceof DataProviderInterface); + + $config = $provider->getEmptyConfig(); $config->setFilter($condition->getFilter($model)); $result = $provider->fetchAll($config); + assert($result instanceof CollectionInterface); if (!$recursive && $result->length() === 0) { return []; } @@ -444,7 +486,7 @@ private function internalCollectChildrenOf(ModelInterface $model, $providerName } } - return \array_merge($ids, ...$childIds); + return \array_values(\array_merge($ids, ...$childIds)); } /** @@ -460,7 +502,13 @@ private function searchParentOfInParentedMode(ModelInterface $model) { $this->guardParentProviderDefined(); - $condition = $this->relationships->getChildCondition($this->parentProviderName, $this->defaultProviderName); + $defaultProviderName = $this->defaultProviderName; + assert(\is_string($defaultProviderName)); + + $parentProviderName = $this->parentProviderName; + assert(\is_string($parentProviderName)); + + $condition = $this->relationships->getChildCondition($parentProviderName, $defaultProviderName); if (null === $condition) { throw new DcGeneralInvalidArgumentException( @@ -468,11 +516,15 @@ private function searchParentOfInParentedMode(ModelInterface $model) ); } + $parentProvider = $this->parentProvider; + assert($parentProvider instanceof DataProviderInterface); + if (null !== ($inverseFilter = $condition->getInverseFilterFor($model))) { - return $this->parentProvider->fetch($this->parentProvider->getEmptyConfig()->setFilter($inverseFilter)); + return $parentProvider->fetch($parentProvider->getEmptyConfig()->setFilter($inverseFilter)); } - - foreach ($this->parentProvider->fetchAll($this->parentProvider->getEmptyConfig()) as $candidate) { + $parentCollection = $parentProvider->fetchAll($parentProvider->getEmptyConfig()); + assert($parentCollection instanceof CollectionInterface); + foreach ($parentCollection as $candidate) { if ($condition->matches($candidate, $model)) { return $candidate; } @@ -508,8 +560,10 @@ private function searchParentOfInHierarchical(ModelInterface $model) } $provider = $this->environment->getDataProvider($condition->getSourceName()); + assert($provider instanceof DataProviderInterface); + $config = $provider->getEmptyConfig()->setFilter($inverseFilter); - $parent = $this->environment->getDataProvider($condition->getSourceName())->fetch($config); + $parent = $provider->fetch($config); if (null !== $parent) { return $parent; @@ -517,9 +571,14 @@ private function searchParentOfInHierarchical(ModelInterface $model) } // Start from the root data provider and walk through the whole tree. // To speed up, some conditions have an inverse filter - we should use them! - $config = $this->rootProvider->getEmptyConfig()->setFilter($this->rootCondition->getFilterArray()); - - return $this->searchParentOfIn($model, $this->rootProvider->fetchAll($config)); + $rootProvider = $this->rootProvider; + assert($rootProvider instanceof DataProviderInterface); + $rootCondition = $this->rootCondition; + assert($rootCondition instanceof RootConditionInterface); + $config = $rootProvider->getEmptyConfig()->setFilter($rootCondition->getFilterArray()); + $parentCollection = $rootProvider->fetchAll($config); + assert($parentCollection instanceof CollectionInterface); + return $this->searchParentOfIn($model, $parentCollection); } /** @@ -542,7 +601,10 @@ private function addParentFilter(ModelInterface $model, $config) // Root model? if ($this->isRootModel($model)) { - $config->setFilter($this->rootCondition->getFilterArray()); + $rootCondition = $this->rootCondition; + assert($rootCondition instanceof RootConditionInterface); + + $config->setFilter($rootCondition->getFilterArray()); return; } @@ -626,7 +688,7 @@ private function guardModelOriginatesFromProvider(ModelInterface $model): void throw new DcGeneralInvalidArgumentException( 'Model originates from ' . $model->getProviderName() . - ' but is expected to be from ' . $this->defaultProviderName . + ' but is expected to be from ' . ($this->defaultProviderName ?? '') . ' can not determine parent.' ); } diff --git a/src/Controller/SortingManager.php b/src/Controller/SortingManager.php index f65ec70b..8763b3c0 100644 --- a/src/Controller/SortingManager.php +++ b/src/Controller/SortingManager.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,7 @@ * @author Christian Schiffler * @author David Molineus * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -32,35 +32,35 @@ class SortingManager /** * The collection containing the models to be inserted. * - * @var CollectionInterface + * @var CollectionInterface|null */ protected $models; /** * The collection containing the models that are siblings. * - * @var CollectionInterface + * @var CollectionInterface|null */ - protected $siblings; + protected $siblings = null; /** * The collection containing the models that are siblings (working copy). * - * @var CollectionInterface + * @var CollectionInterface|null */ protected $siblingsCopy; /** * The result collection. * - * @var CollectionInterface + * @var CollectionInterface|null */ protected $results; /** * The model preceding the target position of the first model from the collection. * - * @var null|ModelInterface + * @var ModelInterface|null */ protected $previousModel; @@ -69,12 +69,12 @@ class SortingManager * * @var string */ - protected $sortingProperty; + protected $sortingProperty = ''; /** * Temporary marker containing the model currently in scope. * - * @var null|ModelInterface + * @var ModelInterface|null */ protected $marker; @@ -83,21 +83,21 @@ class SortingManager * * @var int */ - protected $position; + protected $position = 0; /** * Create a new instance. * - * @param CollectionInterface $models The collection containing the models to be inserted. - * @param CollectionInterface $siblings The collection containing the models that are siblings. - * @param string $sortedBy The property that is used for sorting. - * @param ModelInterface $previousModel The model preceding the target position of the first model from the - * collection. + * @param CollectionInterface|null $models The collection containing the models to be inserted. + * @param CollectionInterface|null $siblings The collection containing the models that are siblings. + * @param string|null $sortedBy The property that is used for sorting. + * @param ModelInterface|null $previousModel The model preceding the target position of the first model from + * the collection. */ public function __construct( CollectionInterface $models = null, CollectionInterface $siblings = null, - $sortedBy = null, + string $sortedBy = null, ModelInterface $previousModel = null ) { if ($models) { @@ -135,7 +135,7 @@ public function setModels(CollectionInterface $models) /** * Get the collection containing the models to be inserted. * - * @return CollectionInterface + * @return CollectionInterface|null */ public function getModels() { @@ -171,7 +171,7 @@ public function getPreviousModel() /** * Get the result collection. * - * @return CollectionInterface + * @return CollectionInterface|null */ public function getResults() { @@ -227,8 +227,11 @@ public function getSortingProperty() */ protected function getModelIds() { - $ids = []; + if (null === $this->models) { + return []; + } + $ids = []; foreach ($this->models as $model) { /** @var ModelInterface $model */ $ids[] = $model->getId(); @@ -248,32 +251,37 @@ protected function scanToDesiredPosition() $this->marker = null; $this->position = 0; $ids = $this->getModelIds(); + + $siblingsCopy = $this->siblingsCopy; + assert($siblingsCopy instanceof CollectionInterface); + // If no previous model, insert at beginning. if (null === $this->previousModel) { - if ($this->siblingsCopy->length()) { - $this->marker = $this->siblingsCopy->shift(); + if ($siblingsCopy->length()) { + $this->marker = $siblingsCopy->shift(); } return; } - if ($this->siblingsCopy->length()) { + $previousModel = $this->getPreviousModel(); + assert($previousModel instanceof ModelInterface); + + if ($siblingsCopy->length()) { // Search for "previous" sibling. do { - $this->marker = $this->siblingsCopy->shift(); - - if (\in_array($this->marker->getId(), $ids)) { - continue; - } - + $this->marker = $siblingsCopy->shift(); if ($this->marker) { + if (\in_array($this->marker->getId(), $ids, true)) { + continue; + } $this->position = $this->marker->getProperty($this->getSortingProperty()); } - } while ($this->marker && $this->marker->getId() !== $this->getPreviousModel()->getId()); + } while ($this->marker && $this->marker->getId() !== $previousModel->getId()); // Remember the "next" sibling. if ($this->marker) { - $this->marker = $this->siblingsCopy->shift(); + $this->marker = $siblingsCopy->shift(); } } } @@ -287,14 +295,20 @@ protected function scanToDesiredPosition() */ private function determineDelta() { + $marker = $this->marker; + assert($marker instanceof ModelInterface); + + $results = $this->results; + assert($results instanceof CollectionInterface); + $delta = ( - ($this->marker->getProperty($this->getSortingProperty()) - $this->position) / $this->results->length() + ($marker->getProperty($this->getSortingProperty()) - $this->position) / $results->length() ); // If delta too narrow, we need to make room. // Prevent delta to exceed, also. Use minimum delta which is calculated as multiple of 128. if (($delta < 2) || ($delta > 128)) { - return (\ceil($this->results->length() / 128) * 128); + return (\ceil($results->length() / 128) * 128); } return $delta; @@ -307,6 +321,8 @@ private function determineDelta() */ private function updateSorting() { + // Called from calculate() only when siblings exist. + assert($this->results instanceof CollectionInterface); $ids = $this->getModelIds(); // If no "next" sibling, simply increment the sorting as we are at the end of the list. if (!$this->marker) { @@ -323,7 +339,7 @@ private function updateSorting() // Loop over all models and increment sorting value. foreach ($this->results as $model) { - $this->position += $delta; + $this->position += (int) $delta; /** @var ModelInterface $model */ $model->setProperty($this->getSortingProperty(), $this->position); } @@ -332,17 +348,20 @@ private function updateSorting() // end of the list. if ($this->marker->getProperty($this->getSortingProperty()) <= $this->position) { do { + $siblingsCopy = $this->siblingsCopy; + assert($siblingsCopy instanceof CollectionInterface); + // Skip models about to be pasted. if (\in_array($this->marker->getId(), $ids)) { - $this->marker = $this->siblingsCopy->shift(); + $this->marker = $siblingsCopy->shift(); continue; } - $this->position += $delta; + $this->position += (int) $delta; $this->marker->setProperty($this->getSortingProperty(), $this->position); $this->results->push($this->marker); - $this->marker = $this->siblingsCopy->shift(); + $this->marker = $siblingsCopy->shift(); } while ($this->marker); } } @@ -356,16 +375,22 @@ private function updateSorting() */ protected function calculate() { - if (isset($this->results) || (0 === $this->models->length())) { + $models = $this->models; + assert($models instanceof CollectionInterface); + + if (isset($this->results) || (0 === $models->length())) { return; } if (!$this->getSortingProperty()) { - throw new \RuntimeException('No sorting property defined for ' . $this->models->get(0)->getProviderName()); + $firstModel = $models->get(0); + assert($firstModel instanceof ModelInterface); + + throw new \RuntimeException('No sorting property defined for ' . $firstModel->getProviderName()); } - $this->results = clone $this->models; - $this->siblingsCopy = clone $this->siblings; + $this->results = clone $models; + $this->siblingsCopy = $this->siblings ? clone $this->siblings : null; $this->scanToDesiredPosition(); $this->updateSorting(); diff --git a/src/Controller/TreeCollector.php b/src/Controller/TreeCollector.php index c0b34537..434026db 100644 --- a/src/Controller/TreeCollector.php +++ b/src/Controller/TreeCollector.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2020 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,18 +14,22 @@ * @author Christian Schiffler * @author Stefan Heimes * @author Sven Baumann - * @copyright 2013-2020 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ namespace ContaoCommunityAlliance\DcGeneral\Controller; +use ContaoCommunityAlliance\DcGeneral\BaseConfigRegistryInterface; use ContaoCommunityAlliance\DcGeneral\Data\CollectionInterface; use ContaoCommunityAlliance\DcGeneral\Data\ConfigInterface; use ContaoCommunityAlliance\DcGeneral\Data\DataProviderInterface; use ContaoCommunityAlliance\DcGeneral\Data\DCGE; use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\BasicDefinitionInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\ModelRelationshipDefinitionInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\ModelRelationship\FilterBuilder; use ContaoCommunityAlliance\DcGeneral\DataDefinition\ModelRelationship\ParentChildConditionInterface; @@ -35,6 +39,8 @@ /** * Generic class to retrieve a tree collection for tree views. + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class TreeCollector implements EnvironmentAwareInterface { @@ -82,7 +88,7 @@ public function __construct( ) { $this->environment = $environment; $this->panel = $panel; - $this->sorting = (array) $sorting; + $this->sorting = $sorting; $this->states = $states; } @@ -144,7 +150,10 @@ private function determineModelState(ModelInterface $model, $level) private function getChildProvidersOf($parentProvider, $relationships = null) { if (null === $relationships) { - $relationships = $this->getEnvironment()->getDataDefinition()->getModelRelationshipDefinition(); + $definition = $this->getEnvironment()->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $relationships = $definition->getModelRelationshipDefinition(); } $mySubTables = []; @@ -164,8 +173,11 @@ private function getChildProvidersOf($parentProvider, $relationships = null) * * @return CollectionInterface|null */ - private function getChildrenOfModel($dataProvider, $model, $childCondition) - { + private function getChildrenOfModel( + DataProviderInterface $dataProvider, + ModelInterface $model, + ParentChildConditionInterface $childCondition + ): ?CollectionInterface { $childIds = $dataProvider ->fetchAll( $dataProvider @@ -173,15 +185,13 @@ private function getChildrenOfModel($dataProvider, $model, $childCondition) ->setFilter($childCondition->getFilter($model)) ->setIdOnly(true) ); + assert(\is_array($childIds)); - if ( - ($childIds instanceof CollectionInterface && !$childIds->count()) - || (\is_array($childIds) && !\count($childIds)) - ) { + if (!\count($childIds)) { return null; } - return $dataProvider->fetchAll( + $children = $dataProvider->fetchAll( $dataProvider ->getEmptyConfig() ->setSorting(['sorting' => 'ASC']) @@ -192,6 +202,10 @@ private function getChildrenOfModel($dataProvider, $model, $childCondition) ->getAllAsArray() ) ); + + assert($children instanceof CollectionInterface); + + return $children; } /** @@ -203,10 +217,14 @@ private function getChildrenOfModel($dataProvider, $model, $childCondition) * * @return void */ - private function treeWalkModel(ModelInterface $model, $intLevel, $subTables = []) + private function treeWalkModel(ModelInterface $model, int $intLevel, array $subTables = []): void { - $environment = $this->getEnvironment(); - $relationships = $environment->getDataDefinition()->getModelRelationshipDefinition(); + $environment = $this->getEnvironment(); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $relationships = $definition->getModelRelationshipDefinition(); $hasChildren = false; $this->determineModelState($model, $intLevel); @@ -223,18 +241,19 @@ private function treeWalkModel(ModelInterface $model, $intLevel, $subTables = [] continue; } - $dataProvider = $environment->getDataProvider($subTable); - $childCollection = $this->getChildrenOfModel($dataProvider, $model, $childFilter); - $hasChildren = (bool) $childCollection; + $dataProvider = $environment->getDataProvider($subTable); + assert($dataProvider instanceof DataProviderInterface); - // Speed up - we may exit if we have at least one child but the parenting model is collapsed. - if ($hasChildren && !$model->getMeta($model::SHOW_CHILDREN)) { - break; - } + $childCollection = $this->getChildrenOfModel($dataProvider, $model, $childFilter); + $hasChildren = null !== $childCollection; if ($hasChildren) { + // Speed up - we may exit if we have at least one child but the parenting model is collapsed. + if (!$model->getMeta($model::SHOW_CHILDREN)) { + break; + } foreach ($childCollection as $childModel) { - // Let the child know about it's parent. + // Let the child know about its parent. $model->setMeta(ModelInterface::PARENT_ID, $model->getID()); $model->setMeta(ModelInterface::PARENT_PROVIDER_NAME, $providerName); @@ -264,29 +283,35 @@ private function treeWalkModel(ModelInterface $model, $intLevel, $subTables = [] */ private function addParentFilter(ConfigInterface $config, ModelInterface $parentModel) { - $environment = $this->getEnvironment(); - $definition = $environment->getDataDefinition(); + $environment = $this->getEnvironment(); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + $basicDefinition = $definition->getBasicDefinition(); + assert($basicDefinition instanceof BasicDefinitionInterface); - if (!$basicDefinition->getParentDataProvider()) { - return; - } + $parentDataProvider = $basicDefinition->getParentDataProvider(); + assert(\is_string($parentDataProvider)); - if ($basicDefinition->getParentDataProvider() !== $parentModel->getProviderName()) { + if ($parentDataProvider !== $parentModel->getProviderName()) { throw new \RuntimeException( \sprintf( 'Parent provider mismatch: %s vs. %s', - $basicDefinition->getParentDataProvider(), + $parentDataProvider, $parentModel->getProviderName() ) ); } + $rootDataProvider = $basicDefinition->getRootDataProvider(); + assert(\is_string($rootDataProvider)); + // Apply parent filtering, do this only for root elements. if ( $parentCondition = $definition->getModelRelationshipDefinition()->getChildCondition( - $basicDefinition->getParentDataProvider(), - $basicDefinition->getRootDataProvider() + $parentDataProvider, + $rootDataProvider ) ) { $baseFilter = $config->getFilter(); @@ -307,9 +332,10 @@ private function addParentFilter(ConfigInterface $config, ModelInterface $parent */ private function calculateRootConfig() { - $rootConfig = $this - ->getEnvironment() - ->getBaseConfigRegistry() + $registry = $this->getEnvironment()->getBaseConfigRegistry(); + assert($registry instanceof BaseConfigRegistryInterface); + + $rootConfig = $registry ->getBaseConfig() ->setSorting($this->getSorting()); $this->getPanel()->initialize($rootConfig); @@ -330,9 +356,11 @@ public function getTreeCollectionRecursive($rootId, $level = 0, $providerName = { $environment = $this->getEnvironment(); $dataProvider = $environment->getDataProvider($providerName); + assert($dataProvider instanceof DataProviderInterface); // Fetch root element. $rootModel = $dataProvider->fetch($this->calculateRootConfig()->setId($rootId)); + assert($rootModel instanceof ModelInterface); $this->treeWalkModel($rootModel, $level, $this->getChildProvidersOf($rootModel->getProviderName())); $rootCollection = $dataProvider->getEmptyCollection(); @@ -352,11 +380,17 @@ public function getTreeCollectionRecursive($rootId, $level = 0, $providerName = */ public function getChildrenOf($providerName, $level = 0, $parentModel = null) { - $environment = $this->getEnvironment(); + $environment = $this->getEnvironment(); + $dataProvider = $environment->getDataProvider($providerName); - $rootConfig = $this->calculateRootConfig(); + assert($dataProvider instanceof DataProviderInterface); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $rootConfig = $this->calculateRootConfig(); - $rootCondition = $environment->getDataDefinition()->getModelRelationshipDefinition()->getRootCondition(); + $rootCondition = $definition->getModelRelationshipDefinition()->getRootCondition(); if (null !== $rootCondition) { $baseFilter = $rootConfig->getFilter(); $filter = $rootCondition->getFilterArray(); @@ -374,6 +408,7 @@ public function getChildrenOf($providerName, $level = 0, $parentModel = null) $rootCollection = $dataProvider->fetchAll($rootConfig); $tableTreeData = $dataProvider->getEmptyCollection(); + assert($rootCollection instanceof CollectionInterface); if ($rootCollection->length() > 0) { $mySubTables = $this->getChildProvidersOf($providerName); diff --git a/src/Controller/TreeNodeStates.php b/src/Controller/TreeNodeStates.php index 9eadadfd..04501d2a 100644 --- a/src/Controller/TreeNodeStates.php +++ b/src/Controller/TreeNodeStates.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,8 @@ * @package contao-community-alliance/dc-general * @author Christian Schiffler * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -32,14 +33,14 @@ class TreeNodeStates * * @var array */ - private $states; + private $states = []; /** * List of implicit open nodes (selected values i.e.). * * @var array */ - private $implicitOpen; + private $implicitOpen = []; /** * Create a new instance. @@ -67,7 +68,7 @@ public function __construct($states = [], $implicitOpen = []) */ public function setStates($states) { - $this->states = (array) $states; + $this->states = $states; return $this; } @@ -96,7 +97,7 @@ public function getStates() */ public function setImplicitOpen($implicitOpen) { - $this->implicitOpen = (array) $implicitOpen; + $this->implicitOpen = $implicitOpen; return $this; } @@ -136,7 +137,7 @@ public function isAllOpen() */ public function setAllOpen($allOpen) { - $this->states['all'] = (bool) $allOpen; + $this->states['all'] = $allOpen; return $this; } @@ -205,7 +206,7 @@ public function setModelState($providerName, $modelId, $state) $this->states[$providerName] = []; } - $this->states[$providerName][$modelId] = (bool) $state; + $this->states[$providerName][$modelId] = $state; return $this; } diff --git a/src/DC/General.php b/src/DC/General.php index d91cacd6..d2f2f504 100644 --- a/src/DC/General.php +++ b/src/DC/General.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -20,7 +20,8 @@ * @author Simon Kusterer * @author David Molineus * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -30,14 +31,17 @@ use Contao\DataContainer; use Contao\System; use ContaoCommunityAlliance\DcGeneral\Action; +use ContaoCommunityAlliance\DcGeneral\Clipboard\ClipboardInterface; use ContaoCommunityAlliance\DcGeneral\Contao\Callback\Callbacks; use ContaoCommunityAlliance\DcGeneral\Controller\ControllerInterface; use ContaoCommunityAlliance\DcGeneral\Data\ModelId; use ContaoCommunityAlliance\DcGeneral\DataContainerInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralRuntimeException; use ContaoCommunityAlliance\DcGeneral\Factory\DcGeneralFactory; use ContaoCommunityAlliance\DcGeneral\Factory\Event\PopulateEnvironmentEvent; +use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; use ContaoCommunityAlliance\DcGeneral\View\ViewInterface; use ContaoCommunityAlliance\Translator\TranslatorInterface; use Symfony\Component\EventDispatcher\EventDispatcherInterface; @@ -82,8 +86,11 @@ public function __construct($tableName, array $module = [], CacheInterface $cach $dispatcher = $this->getEventDispatcher(); $fetcher = \Closure::bind(function (PopulateEnvironmentEvent $event) use ($tableNameCallback) { + $definition = $event->getEnvironment()->getDataDefinition(); + assert($definition instanceof ContainerInterface); + // We need to capture the correct environment and save it for later use. - if ($tableNameCallback !== $event->getEnvironment()->getDataDefinition()->getName()) { + if ($tableNameCallback !== $definition->getName()) { return; } $this->objEnvironment = $event->getEnvironment(); @@ -97,11 +104,11 @@ public function __construct($tableName, array $module = [], CacheInterface $cach ->createDcGeneral(); $dispatcher->removeListener(PopulateEnvironmentEvent::NAME, $fetcher); + $clipboard = $this->getEnvironment()->getClipboard(); + assert($clipboard instanceof ClipboardInterface); + // Load the clipboard. - $this - ->getEnvironment() - ->getClipboard() - ->loadFrom($this->getEnvironment()); + $clipboard->loadFrom($this->getEnvironment()); // Execute AJAX request, called from Backend::getBackendModule // we have to do this here, as otherwise the script will exit as it only checks for DC_Table and DC_File @@ -187,6 +194,7 @@ public function __get($name) { $environment = $this->getEnvironment(); $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); switch ($name) { case 'id': @@ -200,7 +208,10 @@ public function __get($name) return ModelId::fromSerialized($inputProvider->getParameter($idParameter))->getId(); case 'table': - return $environment->getDataDefinition()->getName(); + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + return $definition->getName(); default: } @@ -214,7 +225,10 @@ public function __get($name) */ public function getName() { - return $this->getEnvironment()->getDataDefinition()->getName(); + $definition = $this->getEnvironment()->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + return $definition->getName(); } /** @@ -263,7 +277,10 @@ public function getControllerHandler() */ public function __call($name, $arguments) { - return $this->getEnvironment()->getController()->handle(new Action($name, $arguments)); + $controller = $this->getEnvironment()->getController(); + assert($controller instanceof ControllerInterface); + + return $controller->handle(new Action($name, $arguments)); } /** @@ -274,9 +291,16 @@ public function __call($name, $arguments) protected function callAction() { $environment = $this->getEnvironment(); - $action = new Action($environment->getInputProvider()->getParameter('act') ?: 'showAll'); - return $environment->getController()->handle($action); + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + + $controller = $environment->getController(); + assert($controller instanceof ControllerInterface); + + $action = new Action($inputProvider->getParameter('act') ?: 'showAll'); + + return $controller->handle($action); } /** diff --git a/src/Data/AbstractModel.php b/src/Data/AbstractModel.php index 3161d8f0..fb3ac23b 100644 --- a/src/Data/AbstractModel.php +++ b/src/Data/AbstractModel.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -24,7 +25,7 @@ /** * Class AbstractModel. * Abstract base class for data provider models. - * This class implements the setter and getter for meta data. + * This class implements the setter and getter for meta-data. */ abstract class AbstractModel implements ModelInterface { @@ -38,16 +39,16 @@ abstract class AbstractModel implements ModelInterface /** * {@inheritdoc} */ - public function getMeta($metaName) + public function getMeta($strMetaName) { - return ($this->arrMetaInformation[$metaName] ?? null); + return ($this->arrMetaInformation[$strMetaName] ?? null); } /** * {@inheritdoc} */ - public function setMeta($metaName, $value) + public function setMeta($strMetaName, $varValue) { - $this->arrMetaInformation[$metaName] = $value; + $this->arrMetaInformation[$strMetaName] = $varValue; } } diff --git a/src/Data/ConfigInterface.php b/src/Data/ConfigInterface.php index c42ab7d6..0f200413 100644 --- a/src/Data/ConfigInterface.php +++ b/src/Data/ConfigInterface.php @@ -154,7 +154,7 @@ public function setFilter($arrFilter); * * The returning array will be of 'property name' => 'ASC|DESC' nature. * - * @return array + * @return array */ public function getSorting(); @@ -163,7 +163,7 @@ public function getSorting(); * * The array must be of 'property name' => 'ASC|DESC' nature. * - * @param array $arrSorting The sorting array to use. + * @param array $arrSorting The sorting array to use. * * @return ConfigInterface */ @@ -197,7 +197,7 @@ public function get($strKey); /** * Set the additional information. * - * @param string $strKey The name of the information to retrieve. + * @param string $strKey The name of the information to retrieve. * * @param mixed $varValue The value to store. * diff --git a/src/Data/DataProviderInterface.php b/src/Data/DataProviderInterface.php index f402dc37..bf2d883c 100644 --- a/src/Data/DataProviderInterface.php +++ b/src/Data/DataProviderInterface.php @@ -86,7 +86,7 @@ public function fetch(ConfigInterface $config); * * @param ConfigInterface $config The configuration to use. * - * @return CollectionInterface|ModelInterface[]|string[] + * @return CollectionInterface|list */ public function fetchAll(ConfigInterface $config); @@ -170,7 +170,7 @@ public function saveVersion(ModelInterface $model, $username); * @param mixed $mixID The ID of the record. * @param mixed $mixVersion The ID of the version. * - * @return ModelInterface + * @return ModelInterface|null */ public function getVersion($mixID, $mixVersion); diff --git a/src/Data/DefaultCollection.php b/src/Data/DefaultCollection.php index ce332200..f43c9e41 100644 --- a/src/Data/DefaultCollection.php +++ b/src/Data/DefaultCollection.php @@ -65,7 +65,7 @@ class DefaultCollection implements CollectionInterface */ public function length(): int { - return count($this->arrCollection); + return \count($this->arrCollection); } /** @@ -81,7 +81,7 @@ public function count(): int */ public function offsetExists($offset): bool { - return array_key_exists($offset, $this->arrCollection); + return \array_key_exists($offset, $this->arrCollection); } /** @@ -97,6 +97,7 @@ public function offsetGet($offset): ?ModelInterface */ public function offsetSet($offset, $value): void { + assert(\is_int($offset)); $this->arrCollection[$offset] = $value; } @@ -113,7 +114,7 @@ public function offsetUnset($offset): void */ public function get($index): ?ModelInterface { - if (array_key_exists($index, $this->arrCollection)) { + if (\array_key_exists($index, $this->arrCollection)) { return $this->arrCollection[$index]; } @@ -133,8 +134,8 @@ public function push(ModelInterface $model): void */ public function pop(): ?ModelInterface { - if (count($this->arrCollection)) { - return array_pop($this->arrCollection); + if (\count($this->arrCollection)) { + return \array_pop($this->arrCollection); } return null; @@ -146,7 +147,7 @@ public function pop(): ?ModelInterface public function unshift(ModelInterface $model): void { if ($model->hasProperties()) { - array_unshift($this->arrCollection, $model); + \array_unshift($this->arrCollection, $model); } } @@ -155,8 +156,8 @@ public function unshift(ModelInterface $model): void */ public function shift(): ?ModelInterface { - if (count($this->arrCollection)) { - return array_shift($this->arrCollection); + if (\count($this->arrCollection)) { + return \array_shift($this->arrCollection); } return null; @@ -177,7 +178,7 @@ public function insert($index, ModelInterface $model): void */ public function remove($mixedValue): void { - if (is_object($mixedValue)) { + if (\is_object($mixedValue)) { foreach ($this->arrCollection as $collectionIndex => $model) { if ($mixedValue === $model) { unset($this->arrCollection[$collectionIndex]); @@ -187,7 +188,7 @@ public function remove($mixedValue): void unset($this->arrCollection[$mixedValue]); } - $this->arrCollection = array_values($this->arrCollection); + $this->arrCollection = \array_values($this->arrCollection); } /** diff --git a/src/Data/DefaultConfig.php b/src/Data/DefaultConfig.php index 6e21ec84..f88a4d20 100644 --- a/src/Data/DefaultConfig.php +++ b/src/Data/DefaultConfig.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -15,7 +15,8 @@ * @author Tristan Lins * @author Stefan Heimes * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -73,12 +74,12 @@ class DefaultConfig implements ConfigInterface * * @var array|null */ - protected $arrFilter; + protected $arrFilter = null; /** * The properties to use for sorting. * - * @var array(string => string) + * @var array */ protected $arrSorting = []; @@ -87,7 +88,7 @@ class DefaultConfig implements ConfigInterface * * @var array|null */ - protected $arrFields; + protected $arrFields = null; /** * Miscellaneous arbitrary data stored in the config. @@ -106,7 +107,6 @@ class DefaultConfig implements ConfigInterface */ private function __construct() { - return $this; } /** @@ -116,6 +116,7 @@ private function __construct() */ public static function init() { + /** @psalm-suppress UnsafeInstantiation */ return new static(); } @@ -132,13 +133,13 @@ public function getId() /** * Set a specific id for an element to be retrieved. * - * @param mixed $currentId The id of the element to be retrieved. + * @param mixed $mixId The id of the element to be retrieved. * * @return ConfigInterface */ - public function setId($currentId) + public function setId($mixId) { - $this->mixId = $currentId; + $this->mixId = $mixId; return $this; } @@ -147,16 +148,19 @@ public function setId($currentId) * Get list of specific ids to be retrieved. * * @return array + * + * @deprecated Use filters instead. */ public function getIds() { // @codingStandardsIgnoreStart @\trigger_error( 'The method setids in the DefaultConfig is deprecated since 2.1 and will be removed in 3.0.', - E_NOTICE + E_USER_NOTICE ); // @codingStandardsIgnoreEnd + /** @psalm-suppress DeprecatedProperty */ return $this->arrIds; } @@ -166,18 +170,21 @@ public function getIds() * @param array $arrIds The list of ids to be retrieved. * * @return ConfigInterface + * + * @deprecated Use filters instead. */ public function setIds($arrIds) { // @codingStandardsIgnoreStart @\trigger_error( - 'The method setids in the DefaultConfig is deprecated since 2.1 and will be removed in 3.0. - Use set filter + 'The method setids in the DefaultConfig is deprecated since 2.1 and will be removed in 3.0. + Use set filter $dataConfig->setFilter([[\'operation\' => \'IN\', \'property\' => \'id\', \'values\' => [4,3,2,1]]]).', - E_NOTICE + E_USER_NOTICE ); // @codingStandardsIgnoreEnd + /** @psalm-suppress DeprecatedProperty */ $this->arrIds = $arrIds; return $this; @@ -198,13 +205,13 @@ public function getIdOnly() /** * Set flag for return id only. * - * @param boolean $idOnly Boolean flag to determine that only Ids shall be returned when calling fetchAll(). + * @param boolean $blnIdOnly Boolean flag to determine that only Ids shall be returned when calling fetchAll(). * * @return DefaultConfig */ - public function setIdOnly($idOnly) + public function setIdOnly($blnIdOnly) { - $this->blnIdOnly = $idOnly; + $this->blnIdOnly = $blnIdOnly; return $this; } @@ -226,13 +233,13 @@ public function getStart() * * This is the offset to use for pagination. * - * @param integer $start Number of first element to return. + * @param integer $intStart Number of first element to return. * * @return ConfigInterface */ - public function setStart($start) + public function setStart($intStart) { - $this->intStart = $start; + $this->intStart = $intStart; return $this; } @@ -254,13 +261,13 @@ public function getAmount() * * This is the amount of items to return for pagination. * - * @param int $amount The amount to use. + * @param int $intAmount The amount to use. * * @return ConfigInterface */ - public function setAmount($amount) + public function setAmount($intAmount) { - $this->intAmount = $amount; + $this->intAmount = $intAmount; return $this; } @@ -278,13 +285,13 @@ public function getFilter() /** * Set the list with filter options. * - * @param array $filters The array containing the filter values. + * @param array $arrFilter The array containing the filter values. * * @return ConfigInterface */ - public function setFilter($filters) + public function setFilter($arrFilter) { - $this->arrFilter = $filters; + $this->arrFilter = $arrFilter; return $this; } @@ -294,7 +301,7 @@ public function setFilter($filters) * * The returning array will be of 'property name' => 'ASC|DESC' nature. * - * @return array + * @return array */ public function getSorting() { @@ -306,13 +313,13 @@ public function getSorting() * * The array must be of 'property name' => 'ASC|DESC' nature. * - * @param array $sortingProperties The sorting array to use. + * @param array $arrSorting The sorting array to use. * * @return ConfigInterface */ - public function setSorting($sortingProperties) + public function setSorting($arrSorting) { - $this->arrSorting = $sortingProperties; + $this->arrSorting = $arrSorting; return $this; } @@ -330,13 +337,13 @@ public function getFields() /** * Set the list of fields to be retrieved. * - * @param array $fields Array of property names. + * @param array $arrFields Array of property names. * * @return ConfigInterface */ - public function setFields($fields) + public function setFields($arrFields) { - $this->arrFields = $fields; + $this->arrFields = $arrFields; return $this; } @@ -344,14 +351,14 @@ public function setFields($fields) /** * Get the additional information. * - * @param string $informationName The name of the information to retrieve. + * @param string $strKey The name of the information to retrieve. * * @return mixed || null */ - public function get($informationName) + public function get($strKey) { - if (isset($this->arrData[$informationName])) { - return $this->arrData[$informationName]; + if (isset($this->arrData[$strKey])) { + return $this->arrData[$strKey]; } return null; @@ -360,14 +367,14 @@ public function get($informationName) /** * Set the additional information. * - * @param string $informationName The name of the information to retrieve. - * @param mixed $value The value to store. + * @param string $strKey The name of the information to retrieve. + * @param mixed $varValue The value to store. * * @return ConfigInterface */ - public function set($informationName, $value) + public function set($strKey, $varValue) { - $this->arrData[$informationName] = $value; + $this->arrData[$strKey] = $varValue; return $this; } diff --git a/src/Data/DefaultDataProvider.php b/src/Data/DefaultDataProvider.php index 9d89582a..a8ff5f22 100644 --- a/src/Data/DefaultDataProvider.php +++ b/src/Data/DefaultDataProvider.php @@ -24,6 +24,7 @@ * @author Ingolf Steinhardt * @author Richard Henkenjohann * @author Alex Wuttke + * @author Ingolf Steinhardt * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource @@ -37,6 +38,7 @@ use Contao\System; use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralRuntimeException; use Doctrine\DBAL\Connection; +use Doctrine\DBAL\Exception; use Doctrine\DBAL\Schema\Table; /** @@ -48,6 +50,9 @@ * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) - There is no elegant way to reduce this class more without * reducing the interface. * @SuppressWarnings(PHPMD.CouplingBetweenObjects) - Can not reduce without changing the interface. + * @SuppressWarnings(PHPMD.ExcessiveClassLength) - Can not reduce without changing the interface. + * + * @psalm-suppress MissingConstructor - properties will get set in setBaseConfig(). */ class DefaultDataProvider implements DataProviderInterface { @@ -56,7 +61,7 @@ class DefaultDataProvider implements DataProviderInterface * * @var string */ - protected $source; + protected $source = ''; /** * The Database instance. @@ -75,23 +80,23 @@ class DefaultDataProvider implements DataProviderInterface /** * The property that shall get populated with the current timestamp when saving data. * - * @var string + * @var string|null */ - protected $timeStampProperty = false; + protected $timeStampProperty = null; /** * The id generator to use (if any). * - * @var IdGeneratorInterface + * @var ?IdGeneratorInterface */ - protected $idGenerator; + protected $idGenerator = null; /** * The table schema. * - * @var Table + * @var ?Table */ - private $schema; + private ?Table $schema = null; /** * Retrieve the name of the id property. @@ -130,7 +135,7 @@ public function getTimeStampProperty() /** * Set the property name that shall get updated with the current time stamp when saving to the database. * - * @param boolean $timeStampField The property name or empty to clear. + * @param string $timeStampField The property name or empty to clear. * * @return DefaultDataProvider */ @@ -158,7 +163,7 @@ public function setIdGenerator($idGenerator) /** * Retrieve the id generator. * - * @return IdGeneratorInterface + * @return ?IdGeneratorInterface */ public function getIdGenerator() { @@ -199,6 +204,7 @@ public function setBaseConfig(array $config) throw new DcGeneralRuntimeException('Missing table name.'); } + /** @psalm-suppress DeprecatedMethod - bc layer. */ $this->fallbackFromDatabaseToConnection($config); if (isset($config['connection'])) { @@ -326,6 +332,7 @@ public function delete($item) protected function createModelFromDatabaseResult(array $result) { $model = $this->getEmptyModel(); + assert($model instanceof DefaultModel); foreach ($result as $key => $value) { if ($key === $this->idProperty) { @@ -368,6 +375,9 @@ public function fetch(ConfigInterface $config) } $result = $statement->fetchAssociative(); + if (false === $result) { + return null; + } return $this->createModelFromDatabaseResult($result); } @@ -387,13 +397,16 @@ public function fetchAll(ConfigInterface $config) if (0 !== $config->getAmount()) { $queryBuilder->setMaxResults($config->getAmount()); - $queryBuilder->setFirstResult($config->getStart() ?? 0); + $queryBuilder->setFirstResult($config->getStart()); } $collection = $this->getEmptyCollection(); $statement = $queryBuilder->executeQuery(); if (0 === $statement->rowCount()) { + if ($config->getIdOnly()) { + return []; + } return $collection; } @@ -420,7 +433,7 @@ public function getFilterOptions(ConfigInterface $config) { $internalConfig = $this->prefixDataProviderProperties($config); $properties = $internalConfig->getFields(); - if (1 !== \count($properties)) { + if (null === $properties || 1 !== \count($properties)) { throw new DcGeneralRuntimeException('objConfig must contain exactly one property to be retrieved.'); } $property = $properties[0]; @@ -436,7 +449,7 @@ public function getFilterOptions(ConfigInterface $config) $values = $statement->fetchAllAssociative(); $filterProperties = $config->getFields(); - if (1 !== \count($filterProperties)) { + if (null === $filterProperties || 1 !== \count($filterProperties)) { throw new DcGeneralRuntimeException('objConfig must contain exactly one property to be retrieved.'); } $filterProperty = $filterProperties[0]; @@ -462,9 +475,7 @@ public function getCount(ConfigInterface $config) $queryBuilder->from($this->source); DefaultDataProviderDBalUtils::addWhere($internalConfig, $queryBuilder); - $statement = $queryBuilder->executeQuery(); - - return $statement->fetchFirstColumn(); + return (int) $queryBuilder->executeQuery()->fetchOne(); } /** @@ -480,17 +491,14 @@ public function isUniqueValue($field, $new, $primaryId = null) $queryBuilder->setParameter($field, $new); $statement = $queryBuilder->executeQuery(); - $unique = $statement->fetchAssociative(); - - if (0 === $statement->rowCount()) { + $count = $statement->rowCount(); + if (0 === $count) { return true; } + $unique = $statement->fetchAssociative(); + assert(\is_array($unique)); - if (($primaryId === $unique['id']) && (1 === $statement->rowCount())) { - return true; - } - - return false; + return ($primaryId === $unique['id']) && (1 === $count); } /** @@ -505,7 +513,7 @@ public function resetFallback($field) ); // @codingStandardsIgnoreEnd - $this->connection->query('UPDATE ' . $this->source . ' SET ' . $field . ' = \'\'')->execute(); + $this->connection->executeQuery('UPDATE ' . $this->source . ' SET ' . $field . ' = \'\''); } /** @@ -627,7 +635,7 @@ private function convertModelToDataPropertyArray(ModelInterface $model, int $tim } if ($this->timeStampProperty) { - $data[$this->source . '.' . $this->getTimeStampProperty()] = $timestamp ?: \time(); + $data[$this->source . '.' . ($this->getTimeStampProperty() ?? '')] = $timestamp ?: \time(); } return $data; @@ -644,8 +652,8 @@ private function convertModelToDataPropertyArray(ModelInterface $model, int $tim private function insertModelIntoDatabase(ModelInterface $model, $timestamp = 0): void { $data = $this->convertModelToDataPropertyArray($model, $timestamp); - if ($this->getIdGenerator()) { - $model->setId($this->getIdGenerator()->generate()); + if ($generator = $this->getIdGenerator()) { + $model->setId($generator->generate()); $data[$this->idProperty] = $model->getId(); } @@ -711,8 +719,8 @@ public function saveEach(CollectionInterface $items, $timestamp = 0) */ public function fieldExists($columnName) { - if (!isset($this->schema)) { - $this->schema = $this->connection->getSchemaManager()->listTableDetails($this->source); + if (null === $this->schema) { + $this->schema = $this->connection->createSchemaManager()->introspectTable($this->source); } return $this->schema->hasColumn($columnName); @@ -740,7 +748,8 @@ public function getVersion($mixID, $mixVersion) return null; } - if (null === $version = $statement->fetchAllAssociative()) { + /** @var array{data: string}|false $version */ + if (false === $version = $statement->fetchAssociative()) { return null; } @@ -793,14 +802,14 @@ public function getVersions($mixID, $onlyActive = false) $statement = $queryBuilder->executeQuery(); if (0 === $statement->rowCount()) { - return null; + return $this->getEmptyCollection(); } $versions = $statement->fetchAllAssociative(); $collection = $this->getEmptyCollection(); - foreach ((array) $versions as $versionValue) { + foreach ($versions as $versionValue) { $model = $this->getEmptyModel(); $model->setId($mixID); @@ -839,7 +848,7 @@ public function saveVersion(ModelInterface $model, $username) $queryBuilder->setParameter('fromTable', $this->source); $statement = $queryBuilder->executeQuery(); - $count = $statement->fetchFirstColumn(); + $count = $statement->fetchOne(); $mixNewVersion = ((int) $count + 1); $mixData = $model->getPropertiesAsArray(); @@ -867,6 +876,8 @@ public function saveVersion(ModelInterface $model, $username) * @param mixed $mixVersion The version number to set active. * * @return void + * + * @throws Exception */ public function setVersionActive($mixID, $mixVersion) { @@ -906,7 +917,13 @@ public function getActiveVersion($mixID) return null; } - return $statement->fetchAllAssociative()['version']; + /** @var array{version: int}|false $row */ + $row = $statement->fetchAssociative(); + if (false === $row) { + return null; + } + + return $row['version']; } /** @@ -963,6 +980,9 @@ protected function insertUndo($sourceSQL, $saveSQL, $table) } $result = $statement->fetchAssociative(); + if (false === $result) { + return; + } // Save information in array. $parameters = []; @@ -994,7 +1014,10 @@ protected function insertUndo($sourceSQL, $saveSQL, $table) */ protected function getDefaultConnection() { - return System::getContainer()->get('database_connection'); + $connection = System::getContainer()->get('database_connection'); + assert($connection instanceof Connection); + + return $connection; } /** diff --git a/src/Data/DefaultDataProviderDBalUtils.php b/src/Data/DefaultDataProviderDBalUtils.php index 7abf47e2..0200fffa 100644 --- a/src/Data/DefaultDataProviderDBalUtils.php +++ b/src/Data/DefaultDataProviderDBalUtils.php @@ -26,6 +26,15 @@ /** * Static helper class for DefaultDataProvider. + * + * NOTE: Can't define filters recursively. See: https://github.com/vimeo/psalm/issues/2777 + * @psalm-type TSQLFilterAND=array{operation: 'AND', children: list} + * @psalm-type TSQLFilterOR=array{operation: 'OR', children: list} + * @psalm-type TSQLFilterCMP=array{operation: "="|">"|">="|"<"|"<="|"<>", property: string, value: string|int} + * @psalm-type TSQLFilterIN=array{operation: 'IN'|'NOT IN', property: string, values: list} + * @psalm-type TSQLFilterLIKE=array{operation: 'LIKE'|'NOT LIKE', property: string, value: string} + * @psalm-type TSQLFilterNULL=array{operation: 'IS NULL'|'IS NOT NULL', property: string, value: string} + * @psalm-type TSQLFilter=TSQLFilterAND|TSQLFilterOR|TSQLFilterCMP|TSQLFilterIN|TSQLFilterLIKE|TSQLFilterNULL */ class DefaultDataProviderDBalUtils { @@ -47,19 +56,20 @@ public static function addField(ConfigInterface $config, $idProperty, QueryBuild return; } - if (null !== $config->getFields()) { + if (null !== $fieldList = $config->getFields()) { + /** @psalm-suppress DeprecatedMethod - bc layer */ $connection = $queryBuilder->getConnection(); - $fields = implode( + $fields = \implode( ', ', - array_map( + \array_map( function ($field) use ($connection) { return $connection->quoteIdentifier($field); }, - $config->getFields() + $fieldList ) ); - if (false !== stripos($fields, 'DISTINCT') || \in_array($idProperty, $config->getFields(), true)) { + if (false !== \stripos($fields, 'DISTINCT') || \in_array($idProperty, $fieldList, true)) { $queryBuilder->select($fields); return; } @@ -78,8 +88,6 @@ function ($field) use ($connection) { * @param QueryBuilder $queryBuilder The query builder. * * @return void - * - * @internal param array $parameters The query parameters will get stored into this array. */ public static function addWhere($config, QueryBuilder $queryBuilder) { @@ -96,19 +104,20 @@ public static function addWhere($config, QueryBuilder $queryBuilder) */ public static function addSorting(ConfigInterface $config, QueryBuilder $queryBuilder) { - if (empty($config->getSorting()) || !is_array($config->getSorting())) { + $sorting = $config->getSorting(); + if (empty($sorting)) { return; } - foreach ($config->getSorting() as $sort => $order) { + foreach ($sorting as $sort => $order) { if (empty($sort)) { continue; } // array could be a simple field list or list of field => direction combinations. if (!empty($order)) { - $order = strtoupper($order); - if (!in_array($order, [DCGE::MODEL_SORTING_ASC, DCGE::MODEL_SORTING_DESC])) { + $order = \strtoupper($order); + if (!\in_array($order, [DCGE::MODEL_SORTING_ASC, DCGE::MODEL_SORTING_DESC])) { $sort = $order; $order = DCGE::MODEL_SORTING_ASC; } @@ -127,18 +136,17 @@ public static function addSorting(ConfigInterface $config, QueryBuilder $queryBu * @param QueryBuilder $queryBuilder The query builder. * * @return string The combined conditions. - * - * @internal param array $parameters The query parameters will get stored into this array. */ - private static function addFilter($config, QueryBuilder $queryBuilder) + private static function addFilter(ConfigInterface $config, QueryBuilder $queryBuilder): string { - $result = static::calculateSubFilter( - ['operation' => 'AND', 'children' => $config->getFilter()], - $queryBuilder - ); + /** @var list $filter */ + $filter = $config->getFilter() ?? []; + + /** @var TSQLFilterAND $andFilter */ + $andFilter = ['operation' => 'AND', 'children' => $filter]; // Combine filter syntax. - return $result ?: ''; + return static::calculateSubFilter($andFilter, $queryBuilder); } /** @@ -167,22 +175,16 @@ private static function addFilter($config, QueryBuilder $queryBuilder) * 'property' string (the name of a property) * 'value' literal - Wildcards * (Many) ? (One) * - * @param array $filter The filter to be combined to a valid SQL filter query. + * @param TSQLFilter $filter The filter to be combined to a valid SQL filter query. * @param QueryBuilder $queryBuilder The query builder. * * @return string The combined WHERE conditions. * * @throws DcGeneralRuntimeException If the sub filter has a sub filter. * @throws DcGeneralRuntimeException If the sub filter has a no filter. - * - * @internal param array $parameters The query parameters will get stored into this array. */ - private static function calculateSubFilter($filter, QueryBuilder $queryBuilder) + private static function calculateSubFilter(array $filter, QueryBuilder $queryBuilder): string { - if (!is_array($filter)) { - throw new DcGeneralRuntimeException('Error Processing sub filter: ' . var_export($filter, true), 1); - } - if (null !== ($rule = static::determineFilterAndOr($filter, $queryBuilder))) { return $rule; } @@ -209,16 +211,17 @@ private static function calculateSubFilter($filter, QueryBuilder $queryBuilder) /** * Determine sql filter for and/or. * - * @param array $filter The query filter. + * @param TSQLFilter $filter The query filter. * @param QueryBuilder $queryBuilder The query builder. * * @return string|null */ - private static function determineFilterAndOr(array $filter, QueryBuilder $queryBuilder) + private static function determineFilterAndOr(array $filter, QueryBuilder $queryBuilder): ?string { - if (!\in_array($filter['operation'], ['AND', 'OR'])) { + if (!\in_array($filter['operation'], ['AND', 'OR'], true)) { return null; } + /** @var TSQLFilterAND|TSQLFilterOR $filter */ static::filterAndOr($filter, $queryBuilder); @@ -228,16 +231,17 @@ private static function determineFilterAndOr(array $filter, QueryBuilder $queryB /** * Determine sql filter for comparing. * - * @param array $filter The query filter. + * @param TSQLFilter $filter The query filter. * @param QueryBuilder $queryBuilder The query builder. * * @return string|null */ - private static function determineFilterComparing(array $filter, QueryBuilder $queryBuilder) + private static function determineFilterComparing(array $filter, QueryBuilder $queryBuilder): ?string { - if (!\in_array($filter['operation'], ['=', '>', '>=', '<', '<=', '<>'])) { + if (!\in_array($filter['operation'], ['=', '>', '>=', '<', '<=', '<>'], true)) { return null; } + /** @var TSQLFilterCMP $filter */ return static::filterComparing($filter, $queryBuilder); } @@ -245,16 +249,17 @@ private static function determineFilterComparing(array $filter, QueryBuilder $qu /** * Determine sql filter for in or not in list. * - * @param array $filter The query filter. + * @param TSQLFilter $filter The query filter. * @param QueryBuilder $queryBuilder The query builder. * * @return string|null */ - private static function determineFilterInOrNotInList(array $filter, QueryBuilder $queryBuilder) + private static function determineFilterInOrNotInList(array $filter, QueryBuilder $queryBuilder): ?string { if (!\in_array($filter['operation'], ['IN', 'NOT IN'])) { return null; } + /** @var TSQLFilterIN $filter */ return static::filterInOrNotInList($filter, $queryBuilder); } @@ -262,7 +267,7 @@ private static function determineFilterInOrNotInList(array $filter, QueryBuilder /** * Determine sql filter for like or not like. * - * @param array $filter The query filter. + * @param TSQLFilter $filter The query filter. * @param QueryBuilder $queryBuilder The query builder. * * @return string|null @@ -272,6 +277,7 @@ private static function determineFilterLikeOrNotLike(array $filter, QueryBuilder if (!\in_array($filter['operation'], ['LIKE', 'NOT LIKE'])) { return null; } + /** @var TSQLFilterLIKE $filter */ return static::filterLikeOrNotLike($filter, $queryBuilder); } @@ -279,7 +285,7 @@ private static function determineFilterLikeOrNotLike(array $filter, QueryBuilder /** * Determine sql filter for is null or is not null. * - * @param array $filter The query filter. + * @param TSQLFilter $filter The query filter. * @param QueryBuilder $queryBuilder The query builder. * * @return string|null @@ -289,6 +295,7 @@ private static function determineFilterIsNullOrIsNotNull(array $filter, QueryBui if (!\in_array($filter['operation'], ['IS NULL', 'IS NOT NULL'])) { return null; } + /** @var TSQLFilterNULL $filter */ return static::filterIsNullOrIsNotNull($filter, $queryBuilder); } @@ -296,14 +303,12 @@ private static function determineFilterIsNullOrIsNotNull(array $filter, QueryBui /** * Build an AND or OR query. * - * @param array $operation The operation to convert. - * @param QueryBuilder $queryBuilder The query builder. + * @param TSQLFilterAND|TSQLFilterOR $operation The operation to convert. + * @param QueryBuilder $queryBuilder The query builder. * * @return void - * - * @internal param array $params The parameter array for the resulting query. */ - private static function filterAndOr($operation, QueryBuilder $queryBuilder) + private static function filterAndOr(array $operation, QueryBuilder $queryBuilder): void { $children = $operation['children']; @@ -311,8 +316,9 @@ private static function filterAndOr($operation, QueryBuilder $queryBuilder) return; } - $whereOperation = strtolower($operation['operation']) . 'Where'; + $whereOperation = \strtolower($operation['operation']) . 'Where'; + /** @var TSQLFilter $child */ foreach ($children as $child) { if ('' !== $child = static::calculateSubFilter($child, $queryBuilder)) { $queryBuilder->{$whereOperation}($child); @@ -323,14 +329,12 @@ private static function filterAndOr($operation, QueryBuilder $queryBuilder) /** * Build the sub query for a comparing operator like =,<,>. * - * @param array $operation The operation to apply. - * @param QueryBuilder $queryBuilder The query builder. + * @param TSQLFilterCMP $operation The operation to apply. + * @param QueryBuilder $queryBuilder The query builder. * * @return string - * - * @internal param array $params The parameters of the entire query. */ - private static function filterComparing($operation, QueryBuilder $queryBuilder) + private static function filterComparing(array $operation, QueryBuilder $queryBuilder): string { return $queryBuilder ->expr() @@ -344,16 +348,14 @@ private static function filterComparing($operation, QueryBuilder $queryBuilder) /** * Return the filter query for a "foo IN ('a', 'b')" filter. * - * @param array $operation The operation to apply. + * @param TSQLFilterIN $operation The operation to apply. * @param QueryBuilder $queryBuilder The query builder. * * @return string - * - * @internal param array $params The parameters of the entire query. */ - private static function filterInOrNotInList($operation, QueryBuilder $queryBuilder) + private static function filterInOrNotInList(array $operation, QueryBuilder $queryBuilder): string { - $expressionMethod = lcfirst(preg_replace('/\s+/', '', ucwords(strtolower($operation['operation'])))); + $expressionMethod = \lcfirst(\preg_replace('/\s+/', '', \ucwords(\strtolower($operation['operation'])))); $values = []; foreach ($operation['values'] as $index => $value) { @@ -372,16 +374,14 @@ private static function filterInOrNotInList($operation, QueryBuilder $queryBuild * * The searched value may contain the wildcards '*' and '?' which will get converted to proper SQL. * - * @param array $operation The operation to apply. - * @param QueryBuilder $queryBuilder The query builder. + * @param TSQLFilterLIKE $operation The operation to apply. + * @param QueryBuilder $queryBuilder The query builder. * * @return string - * - * @internal param array $params The parameters of the entire query. */ - private static function filterLikeOrNotLike($operation, QueryBuilder $queryBuilder) + private static function filterLikeOrNotLike(array $operation, QueryBuilder $queryBuilder): string { - $wildcards = str_replace(array('*', '?'), array('%', '_'), $operation['value']); + $wildcards = \str_replace(['*', '?'], ['%', '_'], $operation['value']); return $queryBuilder ->expr() @@ -389,20 +389,18 @@ private static function filterLikeOrNotLike($operation, QueryBuilder $queryBuild } /** - * Return the filter query for a "foo LIKE '%ba_r%'" filter. + * Return the filter query for a "foo IS NULL" filter. * * The searched value may contain the wildcards '*' and '?' which will get converted to proper SQL. * - * @param array $operation The operation to apply. - * @param QueryBuilder $queryBuilder The query builder. + * @param TSQLFilterNULL $operation The operation to apply. + * @param QueryBuilder $queryBuilder The query builder. * * @return string - * - * @internal param array $params The parameters of the entire query. */ - private static function filterIsNullOrIsNotNull($operation, QueryBuilder $queryBuilder) + private static function filterIsNullOrIsNotNull(array $operation, QueryBuilder $queryBuilder): string { - $expressionMethod = lcfirst(preg_replace('/\s+/', '', ucwords(strtolower($operation['operation'])))); + $expressionMethod = \lcfirst(\preg_replace('/\s+/', '', \ucwords(\strtolower($operation['operation'])))); return $queryBuilder ->expr() diff --git a/src/Data/DefaultDataProviderSqlUtils.php b/src/Data/DefaultDataProviderSqlUtils.php index 2faadf72..e3c0b928 100644 --- a/src/Data/DefaultDataProviderSqlUtils.php +++ b/src/Data/DefaultDataProviderSqlUtils.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,8 @@ * @package contao-community-alliance/dc-general * @author Christian Schiffler * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -35,7 +36,6 @@ class DefaultDataProviderSqlUtils * Returns all values from $objConfig->getFields() as comma separated list. * * @param ConfigInterface $config The configuration to use. - * * @param string $idProperty The name of the id property. * * @return string @@ -47,8 +47,9 @@ public static function buildFieldQuery($config, $idProperty) if ($config->getIdOnly()) { return $idProperty; } + if (null !== $config->getFields()) { - $fields = \implode(', ', $config->getFields()); + $fields = \implode(', ', (array) $config->getFields()); if (false !== \stripos($fields, 'DISTINCT')) { return $fields; } @@ -62,7 +63,6 @@ public static function buildFieldQuery($config, $idProperty) * Build the WHERE clause for a configuration. * * @param ConfigInterface $config The configuration to use. - * * @param array $parameters The query parameters will get stored into this array. * * @return string The combined WHERE clause (including the word "WHERE"). @@ -94,7 +94,7 @@ public static function buildSortingQuery($config) $result = ''; $fields = []; - if (empty($sorting) || !\is_array($sorting)) { + if (empty($sorting)) { return ''; } foreach ($sorting as $field => $direction) { @@ -169,12 +169,8 @@ private static function buildFilterQuery($config, array &$parameters) * * @throws DcGeneralRuntimeException If an invalid filter entry is encountered. */ - private static function calculateSubfilter($filter, array &$parameters) + private static function calculateSubfilter(array $filter, array &$parameters): string { - if (!\is_array($filter)) { - throw new DcGeneralRuntimeException('Error Processing sub filter: ' . \var_export($filter, true), 1); - } - switch ($filter['operation']) { case 'AND': case 'OR': @@ -264,9 +260,11 @@ private static function filterInList($operation, &$params) */ private static function filterLike($operation, &$params) { - $wildcards = \str_replace(['*', '?'], ['%', '_'], $operation['value']); + $value = $operation['value']; + assert(\is_string($value)); + $wildcards = \str_replace(['*', '?'], ['%', '_'], $value); $params[] = $wildcards; - return \sprintf('(%s LIKE ?)', $operation['property'], $wildcards); + return \sprintf('(%s LIKE %s)', $operation['property'], $wildcards); } } diff --git a/src/Data/DefaultEditInformation.php b/src/Data/DefaultEditInformation.php index 6655e2fd..e0677b5c 100644 --- a/src/Data/DefaultEditInformation.php +++ b/src/Data/DefaultEditInformation.php @@ -37,7 +37,7 @@ class DefaultEditInformation implements EditInformationInterface /** * The model errors. * - * @var array + * @var array>> */ protected $modelErrors = []; @@ -71,11 +71,7 @@ public function getModelError(ModelInterface $model) { $modelId = ModelId::fromModel($model); - if (!isset($this->modelErrors[$modelId->getSerialized()])) { - return null; - } - - return $this->modelErrors[$modelId->getSerialized()]; + return $this->modelErrors[$modelId->getSerialized()] ?? []; } /** @@ -83,23 +79,24 @@ public function getModelError(ModelInterface $model) */ public function setModelError(ModelInterface $model, array $error, PropertyInterface $property) { - $modelId = ModelId::fromModel($model); + $modelId = ModelId::fromModel($model)->getSerialized(); + $propName = $property->getName(); - if (!isset($this->models[$modelId->getSerialized()])) { - $this->models[$modelId->getSerialized()] = $model; + if (!isset($this->models[$modelId])) { + $this->models[$modelId] = $model; } - if (!isset($this->modelErrors[$modelId->getSerialized()])) { - $this->modelErrors[$modelId->getSerialized()] = []; + if (!isset($this->modelErrors[$modelId])) { + $this->modelErrors[$modelId] = []; } - if (isset($this->modelErrors[$modelId->getSerialized()][$property->getName()])) { - $this->modelErrors[$modelId->getSerialized()][$property->getName()] = \array_merge( - (array) $this->modelErrors[$modelId->getSerialized()][$property->getName()], + if (isset($this->modelErrors[$modelId][$propName])) { + $this->modelErrors[$modelId][$propName] = \array_merge( + $this->modelErrors[$modelId][$propName], $error ); } else { - $this->modelErrors[$modelId->getSerialized()][$property->getName()] = $error; + $this->modelErrors[$modelId][$propName] = $error; } } diff --git a/src/Data/DefaultFilterOptionCollection.php b/src/Data/DefaultFilterOptionCollection.php index 394fd3b7..1941aac3 100644 --- a/src/Data/DefaultFilterOptionCollection.php +++ b/src/Data/DefaultFilterOptionCollection.php @@ -30,7 +30,7 @@ class DefaultFilterOptionCollection implements FilterOptionCollectionInterface /** * The language information stored in this collection. * - * @var array + * @var array */ protected $filterValues = []; @@ -47,7 +47,7 @@ public function add($filterKey, $filterValue) /** * Get a iterator for this collection. * - * @return \Traversable + * @return \Traversable */ public function getIterator(): \Traversable { diff --git a/src/Data/DefaultLanguageInformation.php b/src/Data/DefaultLanguageInformation.php index 4ca04e5e..b0c55b8b 100644 --- a/src/Data/DefaultLanguageInformation.php +++ b/src/Data/DefaultLanguageInformation.php @@ -35,7 +35,7 @@ class DefaultLanguageInformation implements LanguageInformationInterface /** * The ISO 3166 country code. * - * @var string + * @var null|string */ protected $country; @@ -72,8 +72,8 @@ public function getCountryCode() */ public function getLocale() { - if ($this->getCountryCode()) { - return $this->getLanguageCode() . '_' . $this->getCountryCode(); + if (null !== $country = $this->getCountryCode()) { + return $this->getLanguageCode() . '_' . $country; } return $this->getLanguageCode(); diff --git a/src/Data/DefaultLanguageInformationCollection.php b/src/Data/DefaultLanguageInformationCollection.php index ab9ad230..e03a17c8 100644 --- a/src/Data/DefaultLanguageInformationCollection.php +++ b/src/Data/DefaultLanguageInformationCollection.php @@ -29,7 +29,7 @@ class DefaultLanguageInformationCollection implements LanguageInformationCollect /** * The language information stored in this collection. * - * @var LanguageInformationInterface[] + * @var list */ protected $languages = []; diff --git a/src/Data/DefaultModel.php b/src/Data/DefaultModel.php index 85245918..887de68b 100644 --- a/src/Data/DefaultModel.php +++ b/src/Data/DefaultModel.php @@ -17,6 +17,7 @@ * @author Andreas Isaak * @author Patrick Kahl * @author Sven Baumann + * @author Ingolf Steinhardt * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource @@ -36,12 +37,12 @@ class DefaultModel extends AbstractModel /** * A list with all properties. * - * @var array + * @var array */ protected $arrProperties = []; /** - * The Id of this model. + * The ID of this model. * * @var mixed */ @@ -52,7 +53,7 @@ class DefaultModel extends AbstractModel * * @var string */ - protected $strProviderName; + protected $strProviderName = ''; /** * Copy this model, without the id. @@ -109,18 +110,18 @@ public function getPropertiesAsArray() /** * Set the id for this object. * - * NOTE: when the Id has been set once to a non null value, it can NOT be changed anymore. + * NOTE: when the ID has been set once to a non-null value, it can NOT be changed anymore. * - * Normally this should only be called from inside of the implementing provider. + * Normally this should only be called from inside the implementing provider. * - * @param mixed $mixID Could be a integer, string or anything else - depends on the provider implementation. + * @param mixed $mixId Could be integer, string or anything else - depends on the provider implementation. * * @return void */ - public function setID($mixID) + public function setId($mixId) { if (null === $this->mixID) { - $this->setIdRaw($mixID); + $this->setIdRaw($mixId); $this->setMeta(static::IS_CHANGED, true); } } @@ -130,13 +131,13 @@ public function setID($mixID) * * This method is not interfaced and MUST only be used for initial values from the data provider. * - * @param mixed $mixID Could be a integer, string or anything else - depends on the provider implementation. + * @param mixed $mixId Could be a integer, string or anything else - depends on the provider implementation. * * @return void */ - public function setIdRaw($mixID) + public function setIdRaw($mixId) { - $this->mixID = $mixID; + $this->mixID = $mixId; } /** @@ -157,16 +158,16 @@ public function setPropertyRaw($propertyName, $value) /** * Update the property value in the model. * - * @param string $propertyName The property name to be set. - * @param mixed $value The value to be set. + * @param string $strPropertyName The property name to be set. + * @param mixed $varValue The value to be set. * * @return void */ - public function setProperty($propertyName, $value) + public function setProperty($strPropertyName, $varValue) { - if ($value !== $this->getProperty($propertyName)) { + if ($varValue !== $this->getProperty($strPropertyName)) { $this->setMeta(static::IS_CHANGED, true); - $this->setPropertyRaw($propertyName, $value); + $this->setPropertyRaw($strPropertyName, $varValue); } } @@ -179,14 +180,12 @@ public function setProperty($propertyName, $value) */ public function setPropertiesAsArray($properties) { - if (\is_array($properties)) { - if (\array_key_exists('id', $properties)) { - unset($properties['id']); - } + if (\array_key_exists('id', $properties)) { + unset($properties['id']); + } - foreach ($properties as $propertyName => $value) { - $this->setProperty($propertyName, $value); - } + foreach ($properties as $propertyName => $value) { + $this->setProperty($propertyName, $value); } } diff --git a/src/Data/EditInformationInterface.php b/src/Data/EditInformationInterface.php index 8093912b..4d8a2ddb 100644 --- a/src/Data/EditInformationInterface.php +++ b/src/Data/EditInformationInterface.php @@ -39,7 +39,7 @@ public function hasAnyModelError(); * * @param ModelInterface $model The model. * - * @return array + * @return array> */ public function getModelError(ModelInterface $model); @@ -47,7 +47,7 @@ public function getModelError(ModelInterface $model); * Set the error information for the model. * * @param ModelInterface $model The model. - * @param array $error The error information. + * @param list $error The error information. * @param PropertyInterface $property The property. * * @return void diff --git a/src/Data/LanguageInformationCollectionInterface.php b/src/Data/LanguageInformationCollectionInterface.php index 3b9d34d4..04e8600b 100644 --- a/src/Data/LanguageInformationCollectionInterface.php +++ b/src/Data/LanguageInformationCollectionInterface.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,8 @@ * @package contao-community-alliance/dc-general * @author Christian Schiffler * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -22,6 +23,8 @@ /** * This represents an iterable collection of Model elements. + * + * @extends \IteratorAggregate */ interface LanguageInformationCollectionInterface extends \IteratorAggregate, \Countable { diff --git a/src/Data/LanguageInformationInterface.php b/src/Data/LanguageInformationInterface.php index 3575469d..6d2e85dc 100644 --- a/src/Data/LanguageInformationInterface.php +++ b/src/Data/LanguageInformationInterface.php @@ -35,7 +35,7 @@ public function getLanguageCode(); /** * Retrieve the ISO 3166 country code. * - * @return string + * @return string|null */ public function getCountryCode(); diff --git a/src/Data/ModelId.php b/src/Data/ModelId.php index bffd4428..1fb554e9 100644 --- a/src/Data/ModelId.php +++ b/src/Data/ModelId.php @@ -90,6 +90,7 @@ public function getId() */ public static function fromValues($dataProviderName, $modelId) { + /** @psalm-suppress UnsafeInstantiation */ return new static($dataProviderName, $modelId); } diff --git a/src/Data/ModelInterface.php b/src/Data/ModelInterface.php index 6bfe1251..37e2a979 100644 --- a/src/Data/ModelInterface.php +++ b/src/Data/ModelInterface.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -15,7 +15,8 @@ * @author Stefan Heimes * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -28,6 +29,8 @@ * Interface ModelInterface. * * This interface describes a model used in data providers. + * + * @extends \IteratorAggregate */ interface ModelInterface extends \IteratorAggregate { @@ -115,14 +118,14 @@ public function getId(); * * This method returns null if an unknown property is retrieved. * - * @param string $strPropertyName The property name to be retrieved. + * @param string $propertyName The property name to be retrieved. * * @return mixed The value of the given property. */ - public function getProperty($strPropertyName); + public function getProperty($propertyName); /** - * Fetch all properties from the model as an name => value array. + * Fetch all properties from the model as a name => value array. * * @return array */ @@ -140,11 +143,11 @@ public function getMeta($strMetaName); /** * Set the id for this object. * - * NOTE: when the Id has been set once to a non null value, it can NOT be changed anymore. + * NOTE: when the ID has been set once to a non-null value, it can NOT be changed anymore. * - * Normally this should only be called from inside of the implementing provider. + * Normally this should only be called from inside the implementing provider. * - * @param mixed $mixId Could be a integer, string or anything else - depends on the provider implementation. + * @param mixed $mixId Could be integer, string or anything else - depends on the provider implementation. * * @return void */ @@ -163,11 +166,11 @@ public function setProperty($strPropertyName, $varValue); /** * Update all properties in the model. * - * @param array $arrProperties The property values as name => value pairs. + * @param array $properties The property values as name => value pairs. * * @return void */ - public function setPropertiesAsArray($arrProperties); + public function setPropertiesAsArray($properties); /** * Update meta information in the model. diff --git a/src/Data/MultiLanguageDataProvider.php b/src/Data/MultiLanguageDataProvider.php index c90e8c3b..a0f93f48 100644 --- a/src/Data/MultiLanguageDataProvider.php +++ b/src/Data/MultiLanguageDataProvider.php @@ -28,6 +28,8 @@ * Implementation of a multi language Contao database data provider. * * The default language will be initialized to "en". + * + * @psalm-suppress PropertyNotSetInConstructor - properties will get set in setBaseConfig(). */ class MultiLanguageDataProvider extends DefaultDataProvider implements MultiLanguageDataProviderInterface { diff --git a/src/Data/MultiLanguageDataProviderInterface.php b/src/Data/MultiLanguageDataProviderInterface.php index bbdd5e69..4759735c 100644 --- a/src/Data/MultiLanguageDataProviderInterface.php +++ b/src/Data/MultiLanguageDataProviderInterface.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -49,11 +50,11 @@ public function getFallbackLanguage($mixID); /** * Set the current working language for the whole data provider. * - * @param string $strLanguage The new language, use short tag "2 chars like de, fr etc.". + * @param string $language The new language, use short tag "2 chars like de, fr etc.". * * @return DataProviderInterface */ - public function setCurrentLanguage($strLanguage); + public function setCurrentLanguage($language); /** * Get the current working language. diff --git a/src/Data/NoOpDataProvider.php b/src/Data/NoOpDataProvider.php index 6beb8f0a..02bc7782 100644 --- a/src/Data/NoOpDataProvider.php +++ b/src/Data/NoOpDataProvider.php @@ -27,6 +27,8 @@ * * Base implementation of an no operational data provider. This data provider is simply a stub endpoint without any * logic at all. It is useful as parent class for drivers that only implement a fraction of all DcGeneral features. + * + * @psalm-suppress MissingConstructor - properties will get set in setBaseConfig(). */ class NoOpDataProvider implements DataProviderInterface { @@ -72,7 +74,7 @@ public function getEmptyCollection() } /** - * {@inheritdoc} + * @return DefaultFilterOptionCollection */ public function getEmptyFilterOptionCollection() { @@ -122,6 +124,7 @@ public function getCount(ConfigInterface $config) */ public function save(ModelInterface $item, $timestamp = 0) { + return $item; } /** @@ -161,7 +164,7 @@ public function getVersion($mixID, $mixVersion) */ public function getVersions($mixID, $onlyActive = false) { - return null; + return new DefaultCollection(); } /** diff --git a/src/Data/PropertyValueBag.php b/src/Data/PropertyValueBag.php index 166592c9..a9f9b043 100644 --- a/src/Data/PropertyValueBag.php +++ b/src/Data/PropertyValueBag.php @@ -34,27 +34,27 @@ class PropertyValueBag implements PropertyValueBagInterface /** * All properties and its values in this bag. * - * @var array + * @var array */ protected $properties = []; /** * All properties that are marked as invalid and their error messages. * - * @var array + * @var array> */ protected $errors = []; /** * Create a new instance of a property bag. * - * @param array|null $properties The initial property values to use. + * @param iterable|null|mixed $properties The initial property values to use. * * @throws DcGeneralInvalidArgumentException If the passed properties aren't null or an array. */ public function __construct($properties = null) { - if (\is_array($properties) || $properties instanceof \Traversable) { + if (\is_iterable($properties)) { foreach ($properties as $property => $value) { $this->setPropertyValue($property, $value); } @@ -200,7 +200,7 @@ public function getInvalidPropertyNames() public function getPropertyValueErrors($property) { $this->requirePropertyValue($property); - return (array) ($this->errors[$property] ?? []); + return $this->errors[$property] ?? []; } /** @@ -248,6 +248,7 @@ public function offsetGet($offset): mixed */ public function offsetSet($offset, $value): void { + assert(\is_string($offset)); $this->setPropertyValue($offset, $value); } @@ -260,7 +261,7 @@ public function offsetUnset($offset): void } /** - * {@inheritdoc} + * @param string $name */ public function __isset($name) { @@ -268,7 +269,7 @@ public function __isset($name) } /** - * {@inheritdoc} + * @param string $name */ public function __get($name) { @@ -276,7 +277,8 @@ public function __get($name) } /** - * {@inheritdoc} + * @param string $name + * @param mixed $value */ public function __set($name, $value) { @@ -284,7 +286,7 @@ public function __set($name, $value) } /** - * {@inheritdoc} + * @param string $name */ public function __unset($name) { @@ -294,7 +296,7 @@ public function __unset($name) /** * Exports the {@link PropertyValueBag} to an array. * - * @return array + * @return array */ public function getArrayCopy() { diff --git a/src/Data/PropertyValueBagInterface.php b/src/Data/PropertyValueBagInterface.php index 100b5f30..41a7de09 100644 --- a/src/Data/PropertyValueBagInterface.php +++ b/src/Data/PropertyValueBagInterface.php @@ -24,6 +24,9 @@ /** * A generic bag containing properties and their values. + * + * @extends \IteratorAggregate + * @extends \ArrayAccess */ interface PropertyValueBagInterface extends \IteratorAggregate, \Countable, \ArrayAccess { @@ -103,9 +106,9 @@ public function isPropertyValueValid($property); /** * Mark a property as invalid and add an error message to the property. * - * @param string $property The name of the property to mark. - * @param string|array|mixed $error The error message to attach for this property. - * @param bool $append Append this error and keep previous errors (optional). + * @param string $property The name of the property to mark. + * @param string|list $error The error message to attach for this property. + * @param bool $append Append this error and keep previous errors (optional). * * @return PropertyValueBag */ @@ -132,7 +135,7 @@ public function getInvalidPropertyNames(); * * @param string $property The name of the property to retrieve the errors for. * - * @return array + * @return list */ public function getPropertyValueErrors($property); @@ -146,7 +149,7 @@ public function getInvalidPropertyErrors(); /** * Exports the {@link PropertyValueBag} to an array. * - * @return array + * @return array */ public function getArrayCopy(); } diff --git a/src/Data/TableRowsAsRecordsDataProvider.php b/src/Data/TableRowsAsRecordsDataProvider.php index 86fe9a2a..2a789e93 100644 --- a/src/Data/TableRowsAsRecordsDataProvider.php +++ b/src/Data/TableRowsAsRecordsDataProvider.php @@ -31,6 +31,8 @@ * Class TableRowsAsRecordsDataProvider. * * This data provider allows to map multiple rows of a SQL table into a single model for usage in a MultiColumnWizard. + * + * @psalm-suppress MissingConstructor - properties will get set in setBaseConfig(). */ class TableRowsAsRecordsDataProvider extends DefaultDataProvider { @@ -102,7 +104,7 @@ public function getSortingColumnProperty() * @throws DcGeneralException Throws always an exception telling that the method (see param $strMethod) must not be * called. * - * @return void + * @return never */ protected function youShouldNotCallMe($strMethod) { @@ -120,7 +122,7 @@ protected function youShouldNotCallMe($strMethod) * * @param mixed $item Unused. * - * @return void + * @return never * * @throws DcGeneralException Always throws exception. * @@ -164,6 +166,7 @@ public function fetch(ConfigInterface $config) $statement = $queryBuilder->executeQuery(); $model = $this->getEmptyModel(); + assert($model instanceof DefaultModel); $model->setID($config->getId()); if (0 < $statement->rowCount()) { $model->setPropertyRaw('rows', $statement->fetchAllAssociative()); @@ -177,7 +180,7 @@ public function fetch(ConfigInterface $config) * * @param ConfigInterface $config Unused. * - * @return void + * @return never * * @throws DcGeneralException Always throws exception. * @@ -193,7 +196,7 @@ public function fetchAll(ConfigInterface $config) * * @param ConfigInterface $config Unused. * - * @return void + * @return never * * @throws DcGeneralException Always throws exception. * @@ -211,7 +214,7 @@ public function getCount(ConfigInterface $config) * @param mixed $new Unused. * @param int $primaryId Unused. * - * @return void + * @return never * * @throws DcGeneralException Always throws exception. * @@ -227,7 +230,7 @@ public function isUniqueValue($field, $new, $primaryId = null) * * @param string $field Unused. * - * @return void + * @return never * * @throws DcGeneralException Always throws exception. * @@ -262,9 +265,6 @@ public function resetFallback($field) */ public function save(ModelInterface $item, $timestamp = 0, $recursive = false) { - if (!\is_int($timestamp)) { - throw new DcGeneralException('The parameter for this method has been change!'); - } $data = $item->getProperty('rows'); if (!($data && $item->getId())) { throw new DcGeneralException('invalid input data in model.', 1); @@ -292,7 +292,7 @@ public function save(ModelInterface $item, $timestamp = 0, $recursive = false) $sqlData, ['id' => $intId, $this->strGroupCol => $item->getId()] ); - $keep[] = $intId; + $keep[] = (string) $intId; continue; } @@ -301,8 +301,10 @@ public function save(ModelInterface $item, $timestamp = 0, $recursive = false) $sqlData[$this->strGroupCol] = $item->getId(); $this->connection->insert($this->source, $sqlData); - - $keep[] = $this->connection->lastInsertId($this->source); + if (false === $lastInsertId = $this->connection->lastInsertId($this->source)) { + throw new \RuntimeException('Failed to insert'); + } + $keep[] = (string) $lastInsertId; } // House keeping, kill the rest. @@ -323,7 +325,7 @@ public function save(ModelInterface $item, $timestamp = 0, $recursive = false) * @param CollectionInterface $items Unused. * @param int $timestamp Optional the timestamp. * - * @return void + * @return never * * @throws DcGeneralException Always throws exception. * @@ -354,7 +356,7 @@ public function fieldExists($columnName) * @param mixed $mixID Unused. * @param mixed $mixVersion Unused. * - * @return void + * @return never * * @throws DcGeneralException Always throws exception. * @@ -371,14 +373,14 @@ public function getVersion($mixID, $mixVersion) * @param mixed $mixID Unused. * @param boolean $onlyActive Unused. * - * @return null + * @return CollectionInterface * * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function getVersions($mixID, $onlyActive = false) { // Sorry, versioning not supported. - return null; + return new DefaultCollection(); } /** @@ -387,7 +389,7 @@ public function getVersions($mixID, $onlyActive = false) * @param ModelInterface $model Unused. * @param string $username Unused. * - * @return void + * @return never * * @throws DcGeneralException Always throws exception. * @@ -404,7 +406,7 @@ public function saveVersion(ModelInterface $model, $username) * @param mixed $mixID Unused. * @param mixed $mixVersion Unused. * - * @return void + * @return never * * @throws DcGeneralException Always throws exception. * @@ -420,7 +422,7 @@ public function setVersionActive($mixID, $mixVersion) * * @param mixed $mixID Unused. * - * @return void + * @return never * * @throws DcGeneralException Always throws exception. * @@ -437,7 +439,7 @@ public function getActiveVersion($mixID) * @param ModelInterface $firstModel Unused. * @param ModelInterface $secondModel Unused. * - * @return void + * @return never * * @throws DcGeneralException Always throws exception. * @@ -455,7 +457,7 @@ public function sameModels($firstModel, $secondModel) * @param string $saveSQL Unused. * @param string $table Unused. * - * @return void + * @return never * * @throws DcGeneralException Always throws exception. * diff --git a/src/DataDefinition/AbstractConditionChain.php b/src/DataDefinition/AbstractConditionChain.php index 7facf388..1deda72f 100644 --- a/src/DataDefinition/AbstractConditionChain.php +++ b/src/DataDefinition/AbstractConditionChain.php @@ -122,7 +122,7 @@ public function setConjunction($conjunction) ); } - $this->conjunction = (string) $conjunction; + $this->conjunction = $conjunction; return $this; } diff --git a/src/DataDefinition/Builder/AbstractEventDrivenDataDefinitionBuilder.php b/src/DataDefinition/Builder/AbstractEventDrivenDataDefinitionBuilder.php index 2fc3bb39..78e6fee7 100644 --- a/src/DataDefinition/Builder/AbstractEventDrivenDataDefinitionBuilder.php +++ b/src/DataDefinition/Builder/AbstractEventDrivenDataDefinitionBuilder.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -22,6 +23,7 @@ namespace ContaoCommunityAlliance\DcGeneral\DataDefinition\Builder; use ContaoCommunityAlliance\DcGeneral\Factory\Event\BuildDataDefinitionEvent; +use LogicException; use Symfony\Component\EventDispatcher\EventDispatcherInterface; /** @@ -30,6 +32,8 @@ * Abstract base class for an data definition builder. * * To use it, implement the method build() and register the class to the event dispatcher. + * + * @psalm-suppress MissingConstructor - properties will get set in process(). */ abstract class AbstractEventDrivenDataDefinitionBuilder implements DataDefinitionBuilderInterface { @@ -44,16 +48,16 @@ abstract class AbstractEventDrivenDataDefinitionBuilder implements DataDefinitio /** * The event dispatcher currently calling. * - * @var EventDispatcherInterface + * @var EventDispatcherInterface|null */ - protected $dispatcher; + protected $dispatcher = null; /** * The name of the called event. * * @var string */ - protected $eventName; + protected $eventName = ''; /** * Retrieve the dispatcher. @@ -62,6 +66,10 @@ abstract class AbstractEventDrivenDataDefinitionBuilder implements DataDefinitio */ protected function getDispatcher() { + if (null === $this->dispatcher) { + throw new LogicException('Dispatcher not set.'); + } + return $this->dispatcher; } @@ -89,6 +97,7 @@ protected function getDispatchedEventName() */ public static function process(BuildDataDefinitionEvent $event, $eventName, $dispatcher) { + /** @psalm-suppress UnsafeInstantiation */ $builder = new static(); $builder->eventName = $eventName; $builder->dispatcher = $dispatcher; diff --git a/src/DataDefinition/DataProviderInformation.php b/src/DataDefinition/DataProviderInformation.php index adb9f287..457788f8 100644 --- a/src/DataDefinition/DataProviderInformation.php +++ b/src/DataDefinition/DataProviderInformation.php @@ -30,14 +30,14 @@ class DataProviderInformation implements DataProviderInformationInterface * * @var string */ - protected $name; + protected $name = ''; /** * Flag determining if versioning is enabled for this provider or not. * * @var bool */ - protected $versioningEnabled; + protected $versioningEnabled = false; /** * {@inheritdoc} diff --git a/src/DataDefinition/DefaultContainer.php b/src/DataDefinition/DefaultContainer.php index fd77d85a..5cf9b821 100644 --- a/src/DataDefinition/DefaultContainer.php +++ b/src/DataDefinition/DefaultContainer.php @@ -57,7 +57,7 @@ class DefaultContainer implements ContainerInterface */ public function __construct($name) { - $this->name = (string) $name; + $this->name = $name; } /** @@ -171,9 +171,9 @@ public function hasBasicDefinition() /** * {@inheritdoc} */ - public function setBasicDefinition(BasicDefinitionInterface $definition) + public function setBasicDefinition(BasicDefinitionInterface $basicDefinition) { - return $this->setDefinition(BasicDefinitionInterface::NAME, $definition); + return $this->setDefinition(BasicDefinitionInterface::NAME, $basicDefinition); } /** @@ -181,7 +181,10 @@ public function setBasicDefinition(BasicDefinitionInterface $definition) */ public function getBasicDefinition() { - return $this->getDefinition(BasicDefinitionInterface::NAME); + $definition = $this->getDefinition(BasicDefinitionInterface::NAME); + assert($definition instanceof BasicDefinitionInterface); + + return $definition; } /** @@ -195,9 +198,9 @@ public function hasPropertiesDefinition() /** * {@inheritdoc} */ - public function setPropertiesDefinition(PropertiesDefinitionInterface $definition) + public function setPropertiesDefinition(PropertiesDefinitionInterface $propertiesDefinition) { - return $this->setDefinition(PropertiesDefinitionInterface::NAME, $definition); + return $this->setDefinition(PropertiesDefinitionInterface::NAME, $propertiesDefinition); } /** @@ -205,7 +208,10 @@ public function setPropertiesDefinition(PropertiesDefinitionInterface $definitio */ public function getPropertiesDefinition() { - return $this->getDefinition(PropertiesDefinitionInterface::NAME); + $definition = $this->getDefinition(PropertiesDefinitionInterface::NAME); + assert($definition instanceof PropertiesDefinitionInterface); + + return $definition; } /** @@ -219,9 +225,9 @@ public function hasPalettesDefinition() /** * {@inheritdoc} */ - public function setPalettesDefinition(PalettesDefinitionInterface $definition) + public function setPalettesDefinition(PalettesDefinitionInterface $palettesDefinition) { - return $this->setDefinition(PalettesDefinitionInterface::NAME, $definition); + return $this->setDefinition(PalettesDefinitionInterface::NAME, $palettesDefinition); } /** @@ -229,7 +235,10 @@ public function setPalettesDefinition(PalettesDefinitionInterface $definition) */ public function getPalettesDefinition() { - return $this->getDefinition(PalettesDefinitionInterface::NAME); + $definition = $this->getDefinition(PalettesDefinitionInterface::NAME); + assert($definition instanceof PalettesDefinitionInterface); + + return $definition; } /** @@ -253,7 +262,10 @@ public function setDataProviderDefinition(DataProviderDefinitionInterface $defin */ public function getDataProviderDefinition() { - return $this->getDefinition(DataProviderDefinitionInterface::NAME); + $definition = $this->getDefinition(DataProviderDefinitionInterface::NAME); + assert($definition instanceof DataProviderDefinitionInterface); + + return $definition; } /** @@ -277,7 +289,10 @@ public function setModelRelationshipDefinition(ModelRelationshipDefinitionInterf */ public function getModelRelationshipDefinition() { - return $this->getDefinition(ModelRelationshipDefinitionInterface::NAME); + $definition = $this->getDefinition(ModelRelationshipDefinitionInterface::NAME); + assert($definition instanceof ModelRelationshipDefinitionInterface); + + return $definition; } /** diff --git a/src/DataDefinition/Definition/BasicDefinitionInterface.php b/src/DataDefinition/Definition/BasicDefinitionInterface.php index 96db9770..85429a36 100644 --- a/src/DataDefinition/Definition/BasicDefinitionInterface.php +++ b/src/DataDefinition/Definition/BasicDefinitionInterface.php @@ -276,7 +276,7 @@ public function getRootEntries(); * * @param bool $dynamicParentTable The flag - if the data container is an dynamic. * - * @return bool + * @return BasicDefinitionInterface */ public function setDynamicParentTable($dynamicParentTable); diff --git a/src/DataDefinition/Definition/DataProviderDefinitionInterface.php b/src/DataDefinition/Definition/DataProviderDefinitionInterface.php index 61b93c42..aefedf94 100644 --- a/src/DataDefinition/Definition/DataProviderDefinitionInterface.php +++ b/src/DataDefinition/Definition/DataProviderDefinitionInterface.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -25,6 +26,9 @@ /** * This interface describes a collection of data provider information. + * + * @extends \IteratorAggregate + * @extends \ArrayAccess */ interface DataProviderDefinitionInterface extends DefinitionInterface, \IteratorAggregate, \Countable, \ArrayAccess { diff --git a/src/DataDefinition/Definition/DefaultBasicDefinition.php b/src/DataDefinition/Definition/DefaultBasicDefinition.php index 66e84858..820a1ba3 100644 --- a/src/DataDefinition/Definition/DefaultBasicDefinition.php +++ b/src/DataDefinition/Definition/DefaultBasicDefinition.php @@ -94,9 +94,9 @@ class DefaultBasicDefinition implements BasicDefinitionInterface /** * Determines if the view shall switch automatically into edit mode. * - * @var bool|null + * @var bool */ - protected $switchToEditEnabled; + protected $switchToEditEnabled = false; /** * The ids of the root entries. @@ -224,6 +224,8 @@ public function getAdditionalFilter($dataProvider = null) public function setEditOnlyMode($value) { $this->isEditOnlyMode = $value; + + return $this; } /** diff --git a/src/DataDefinition/Definition/DefaultDataProviderDefinition.php b/src/DataDefinition/Definition/DefaultDataProviderDefinition.php index 18bc04a6..e7d72205 100644 --- a/src/DataDefinition/Definition/DefaultDataProviderDefinition.php +++ b/src/DataDefinition/Definition/DefaultDataProviderDefinition.php @@ -35,7 +35,7 @@ class DefaultDataProviderDefinition implements DataProviderDefinitionInterface /** * The data provider information stored in the definition. * - * @var DataProviderInformationInterface[] + * @var array */ protected $information = []; @@ -58,6 +58,8 @@ public function addInformation($information) } $this->information[$name] = $information; + + return $this; } /** @@ -80,10 +82,6 @@ protected function makeName($information) $information = $information->getName(); } - if (!\is_string($information)) { - throw new DcGeneralInvalidArgumentException('Invalid value passed.'); - } - return $information; } @@ -93,6 +91,8 @@ protected function makeName($information) public function removeInformation($information) { unset($this->information[$this->makeName($information)]); + + return $this; } /** @@ -101,6 +101,8 @@ public function removeInformation($information) public function setInformation($name, $information) { $this->information[$name] = $information; + + return $this; } /** @@ -164,6 +166,7 @@ public function offsetGet($offset): mixed */ public function offsetSet($offset, $value): void { + assert(\is_string($offset)); $this->setInformation($offset, $value); } @@ -176,7 +179,7 @@ public function offsetUnset($offset): void } /** - * {@inheritdoc} + * @param string $name */ public function __isset($name) { @@ -184,7 +187,7 @@ public function __isset($name) } /** - * {@inheritdoc} + * @param string $name */ public function __get($name) { @@ -192,7 +195,8 @@ public function __get($name) } /** - * {@inheritdoc} + * @param string $name + * @param mixed $value */ public function __set($name, $value) { @@ -200,7 +204,7 @@ public function __set($name, $value) } /** - * {@inheritdoc} + * @param string $name */ public function __unset($name) { diff --git a/src/DataDefinition/Definition/DefaultModelRelationshipDefinition.php b/src/DataDefinition/Definition/DefaultModelRelationshipDefinition.php index 151ae9cf..54db3a07 100644 --- a/src/DataDefinition/Definition/DefaultModelRelationshipDefinition.php +++ b/src/DataDefinition/Definition/DefaultModelRelationshipDefinition.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,8 @@ * @package contao-community-alliance/dc-general * @author Christian Schiffler * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -31,9 +32,9 @@ class DefaultModelRelationshipDefinition implements ModelRelationshipDefinitionI /** * The root condition relationship. * - * @var RootConditionInterface + * @var RootConditionInterface|null */ - protected $rootCondition; + protected $rootCondition = null; /** * A collection of parent child conditions. diff --git a/src/DataDefinition/Definition/DefaultPropertiesDefinition.php b/src/DataDefinition/Definition/DefaultPropertiesDefinition.php index 0f655987..770913ea 100644 --- a/src/DataDefinition/Definition/DefaultPropertiesDefinition.php +++ b/src/DataDefinition/Definition/DefaultPropertiesDefinition.php @@ -106,7 +106,7 @@ public function hasProperty($name) /** * {@inheritdoc} * - * @throws DcGeneralInvalidArgumentException When an a property with the given name has not been registered. + * @throws DcGeneralInvalidArgumentException When a property with the given name has not been registered. */ public function getProperty($name) { diff --git a/src/DataDefinition/Definition/Properties/DefaultProperty.php b/src/DataDefinition/Definition/Properties/DefaultProperty.php index afb82280..316d0b53 100644 --- a/src/DataDefinition/Definition/Properties/DefaultProperty.php +++ b/src/DataDefinition/Definition/Properties/DefaultProperty.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -35,21 +36,21 @@ class DefaultProperty implements PropertyInterface, EmptyValueAwarePropertyInter * * @var string */ - protected $name; + protected $name = ''; /** * The label of the property. * * @var string */ - protected $label; + protected $label = ''; /** * The description of the property. * * @var string */ - protected $description; + protected $description = ''; /** * The default value of the property. @@ -63,60 +64,35 @@ class DefaultProperty implements PropertyInterface, EmptyValueAwarePropertyInter * * @var bool */ - protected $excluded; + protected $excluded = false; /** * Flag if this property shall be searchable. * * @var bool */ - protected $searchable; + protected $searchable = false; /** * Flag if this property shall be sortable. * * @var bool */ - protected $sortable; + protected $sortable = false; /** * Flag if this property shall be filterable. * * @var bool */ - protected $filterable; - - /** - * The grouping mode for this property. - * - * See ListingConfigInterface::GROUP_* flags. - * - * @var string - */ - protected $groupingMode; - - /** - * The grouing length of this property. See grouping mode. - * - * @var string - */ - protected $groupingLength; - - /** - * The sorting mode for this property. - * - * See ListingConfigInterface::SORT_* flags. - * - * @var string - */ - protected $sortingMode; + protected $filterable = false; /** * The input widget type to use. * * @var string */ - protected $widgetType; + protected $widgetType = ''; /** * The value options for this property. @@ -130,7 +106,7 @@ class DefaultProperty implements PropertyInterface, EmptyValueAwarePropertyInter * * @var string */ - protected $explanation; + protected $explanation = ''; /** * The extra information for this property. diff --git a/src/DataDefinition/Definition/PropertiesDefinitionInterface.php b/src/DataDefinition/Definition/PropertiesDefinitionInterface.php index d166d006..825e374a 100644 --- a/src/DataDefinition/Definition/PropertiesDefinitionInterface.php +++ b/src/DataDefinition/Definition/PropertiesDefinitionInterface.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -25,6 +26,8 @@ /** * This interface describes the data definition that holds all property information. + * + * @extends \IteratorAggregate */ interface PropertiesDefinitionInterface extends DefinitionInterface, \IteratorAggregate { diff --git a/src/DataDefinition/Definition/View/Command.php b/src/DataDefinition/Definition/View/Command.php index 2c72c6b3..a275b127 100644 --- a/src/DataDefinition/Definition/View/Command.php +++ b/src/DataDefinition/Definition/View/Command.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -33,7 +34,7 @@ class Command implements CommandInterface * * @var string */ - protected $name; + protected $name = ''; /** * The parameters for the command. @@ -47,14 +48,14 @@ class Command implements CommandInterface * * @var string */ - protected $label; + protected $label = ''; /** * The description text for the command. * * @var string */ - protected $description; + protected $description = ''; /** * The extra data for the command. @@ -68,7 +69,7 @@ class Command implements CommandInterface * * @var bool */ - protected $disabled; + protected $disabled = false; /** * Create a new instance. @@ -84,7 +85,7 @@ public function __construct() */ public function setName($name) { - $this->name = (string) $name; + $this->name = $name; return $this; } @@ -120,7 +121,7 @@ public function getParameters() */ public function setLabel($label) { - $this->label = (string) $label; + $this->label = $label; return $this; } @@ -138,7 +139,7 @@ public function getLabel() */ public function setDescription($description) { - $this->description = (string) $description; + $this->description = $description; return $this; } diff --git a/src/DataDefinition/Definition/View/CommandCollection.php b/src/DataDefinition/Definition/View/CommandCollection.php index 994ba9a8..d1c3a0a4 100644 --- a/src/DataDefinition/Definition/View/CommandCollection.php +++ b/src/DataDefinition/Definition/View/CommandCollection.php @@ -34,7 +34,7 @@ class CommandCollection implements CommandCollectionInterface /** * The commands contained within the collection. * - * @var CommandInterface[] + * @var array */ protected $commands = []; @@ -123,9 +123,9 @@ public function addCommand(CommandInterface $command, CommandInterface $before = $position = \array_search($beforeHash, $hashes); $this->commands = \array_merge( - \array_slice($this->commands, 0, $position), + \array_slice($this->commands, 0, (int) $position), [$hash => $command], - \array_slice($this->commands, $position) + \array_slice($this->commands, (int) $position) ); } else { throw new DcGeneralInvalidArgumentException( diff --git a/src/DataDefinition/Definition/View/CommandCollectionInterface.php b/src/DataDefinition/Definition/View/CommandCollectionInterface.php index b78e9ef5..dd9f2bcb 100644 --- a/src/DataDefinition/Definition/View/CommandCollectionInterface.php +++ b/src/DataDefinition/Definition/View/CommandCollectionInterface.php @@ -124,7 +124,7 @@ public function getCommandNamed($name); /** * Get commands from this collection. * - * @return CommandInterface[]|array + * @return array */ public function getCommands(); } diff --git a/src/DataDefinition/Definition/View/CommandInterface.php b/src/DataDefinition/Definition/View/CommandInterface.php index f0faf407..bf40b27a 100644 --- a/src/DataDefinition/Definition/View/CommandInterface.php +++ b/src/DataDefinition/Definition/View/CommandInterface.php @@ -72,7 +72,7 @@ public function setLabel($label); /** * Return the label of the command. * - * @return array + * @return string */ public function getLabel(); @@ -88,7 +88,7 @@ public function setDescription($description); /** * Return the description of the command. * - * @return array + * @return string */ public function getDescription(); diff --git a/src/DataDefinition/Definition/View/DefaultGroupAndSortingDefinitionCollection.php b/src/DataDefinition/Definition/View/DefaultGroupAndSortingDefinitionCollection.php index 62ee4ae9..57bec044 100644 --- a/src/DataDefinition/Definition/View/DefaultGroupAndSortingDefinitionCollection.php +++ b/src/DataDefinition/Definition/View/DefaultGroupAndSortingDefinitionCollection.php @@ -117,6 +117,8 @@ public function markDefault($information) } $this->default = $information; + + return $this; } /** diff --git a/src/DataDefinition/Definition/View/DefaultGroupAndSortingInformation.php b/src/DataDefinition/Definition/View/DefaultGroupAndSortingInformation.php index 4e2ba6ef..8b136275 100644 --- a/src/DataDefinition/Definition/View/DefaultGroupAndSortingInformation.php +++ b/src/DataDefinition/Definition/View/DefaultGroupAndSortingInformation.php @@ -31,7 +31,7 @@ class DefaultGroupAndSortingInformation implements GroupAndSortingInformationInt * * @var string */ - protected $property; + protected $property = ''; /** * The sorting method to use. @@ -59,7 +59,7 @@ class DefaultGroupAndSortingInformation implements GroupAndSortingInformationInt * * @var bool */ - protected $manualSorting; + protected $manualSorting = false; /** * {@inheritDoc} diff --git a/src/DataDefinition/Definition/View/DefaultListingConfig.php b/src/DataDefinition/Definition/View/DefaultListingConfig.php index cfc5b375..6131ec8d 100644 --- a/src/DataDefinition/Definition/View/DefaultListingConfig.php +++ b/src/DataDefinition/Definition/View/DefaultListingConfig.php @@ -40,11 +40,11 @@ class DefaultListingConfig implements ListingConfigInterface protected $groupAndSorting; /** - * The properties to display in the heder (parented mode only). + * The properties to display in the header (parented mode only). * - * @var array|null + * @var list */ - protected $headerProperties; + protected $headerProperties = []; /** * The root icon to use (hierarchical mode only). @@ -70,7 +70,7 @@ class DefaultListingConfig implements ListingConfigInterface /** * The item formatter to use. * - * @var DefaultModelFormatterConfig[] + * @var array */ protected $itemFormatter = []; diff --git a/src/DataDefinition/Definition/View/DefaultModelFormatterConfig.php b/src/DataDefinition/Definition/View/DefaultModelFormatterConfig.php index 22360968..2e14bdf9 100644 --- a/src/DataDefinition/Definition/View/DefaultModelFormatterConfig.php +++ b/src/DataDefinition/Definition/View/DefaultModelFormatterConfig.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -45,7 +46,7 @@ class DefaultModelFormatterConfig implements ModelFormatterConfigInterface * * @var int|null */ - protected $maxLength; + protected $maxLength = null; /** * {@inheritDoc} @@ -70,7 +71,7 @@ public function getPropertyNames() */ public function setFormat($format) { - $this->format = (string) $format; + $this->format = $format; return $this; } @@ -88,7 +89,7 @@ public function getFormat() */ public function setMaxLength($maxLength) { - $this->maxLength = (null !== $maxLength) ? (int) $maxLength : null; + $this->maxLength = (null !== $maxLength) ? $maxLength : null; return $this; } diff --git a/src/DataDefinition/Definition/View/DefaultPanelRow.php b/src/DataDefinition/Definition/View/DefaultPanelRow.php index 451b28dd..72334906 100644 --- a/src/DataDefinition/Definition/View/DefaultPanelRow.php +++ b/src/DataDefinition/Definition/View/DefaultPanelRow.php @@ -33,7 +33,7 @@ class DefaultPanelRow implements PanelRowInterface /** * The contained elements. * - * @var ElementInformationInterface[] + * @var list */ protected $elements = []; @@ -93,7 +93,7 @@ function ($element) use ($indexOrNameOrInstance) { break; } } - } elseif (\is_numeric($indexOrNameOrInstance)) { + } else { unset($this->elements[$indexOrNameOrInstance]); } @@ -111,18 +111,14 @@ public function hasElement($instanceOrName) return \in_array($instanceOrName, $this->elements); } - if (\is_string($instanceOrName)) { - foreach ($this as $element) { - /** @var ElementInformationInterface $element */ - if ($instanceOrName === $element->getName()) { - return true; - } + foreach ($this as $element) { + /** @var ElementInformationInterface $element */ + if ($instanceOrName === $element->getName()) { + return true; } - - return false; } - throw new DcGeneralInvalidArgumentException('Invalid value for element name given.'); + return false; } /** @@ -148,8 +144,7 @@ public function getElement($indexOrName) return $element; } } - } elseif (!\is_numeric($indexOrName)) { - throw new DcGeneralInvalidArgumentException('Invalid value for element name given.'); + throw new DcGeneralInvalidArgumentException('Value out of bounds: ' . $indexOrName . '.'); } if (!isset($this->elements[$indexOrName])) { diff --git a/src/DataDefinition/Definition/View/DefaultPanelRowCollection.php b/src/DataDefinition/Definition/View/DefaultPanelRowCollection.php index cb1bc81e..1ccfca0a 100644 --- a/src/DataDefinition/Definition/View/DefaultPanelRowCollection.php +++ b/src/DataDefinition/Definition/View/DefaultPanelRowCollection.php @@ -31,7 +31,7 @@ class DefaultPanelRowCollection implements PanelRowCollectionInterface /** * The panel rows. * - * @var PanelRowInterface[] + * @var list */ protected $rows = []; diff --git a/src/DataDefinition/Definition/View/ListingConfigInterface.php b/src/DataDefinition/Definition/View/ListingConfigInterface.php index d7af4a9b..373d381b 100644 --- a/src/DataDefinition/Definition/View/ListingConfigInterface.php +++ b/src/DataDefinition/Definition/View/ListingConfigInterface.php @@ -56,7 +56,7 @@ public function getGroupAndSortingDefinition(); /** * Set the list of parent's model property names. * - * @param array $value The property names to use. + * @param list $value The property names to use. * * @return ListingConfigInterface */ @@ -65,7 +65,7 @@ public function setHeaderPropertyNames($value); /** * Return a list of parent's model property names, which are shown above the item list. * - * @return array + * @return list */ public function getHeaderPropertyNames(); diff --git a/src/DataDefinition/Definition/View/Panel/DefaultFilterElementInformation.php b/src/DataDefinition/Definition/View/Panel/DefaultFilterElementInformation.php index df0b36f1..14e6a8f4 100644 --- a/src/DataDefinition/Definition/View/Panel/DefaultFilterElementInformation.php +++ b/src/DataDefinition/Definition/View/Panel/DefaultFilterElementInformation.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -33,7 +34,7 @@ class DefaultFilterElementInformation implements FilterElementInformationInterfa * * @var string */ - protected $propertyName; + protected $propertyName = ''; /** * {@inheritDoc} @@ -49,6 +50,8 @@ public function getName() public function setPropertyName($propertyName) { $this->propertyName = $propertyName; + + return $this; } /** diff --git a/src/DataDefinition/Definition/View/Panel/DefaultSearchElementInformation.php b/src/DataDefinition/Definition/View/Panel/DefaultSearchElementInformation.php index 6fcf0efc..1324105c 100644 --- a/src/DataDefinition/Definition/View/Panel/DefaultSearchElementInformation.php +++ b/src/DataDefinition/Definition/View/Panel/DefaultSearchElementInformation.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -33,7 +34,7 @@ class DefaultSearchElementInformation implements SearchElementInformationInterfa * * @var array */ - protected $properties; + protected $properties = []; /** * {@inheritDoc} @@ -49,6 +50,8 @@ public function getName() public function addProperty($propertyName) { $this->properties[] = $propertyName; + + return $this; } /** diff --git a/src/DataDefinition/Definition/View/PanelRowCollectionInterface.php b/src/DataDefinition/Definition/View/PanelRowCollectionInterface.php index ec9d33d3..c862b9c0 100644 --- a/src/DataDefinition/Definition/View/PanelRowCollectionInterface.php +++ b/src/DataDefinition/Definition/View/PanelRowCollectionInterface.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -23,6 +24,8 @@ /** * This interface describes a panel row collection. + * + * @extends \IteratorAggregate */ interface PanelRowCollectionInterface extends \IteratorAggregate { @@ -34,7 +37,7 @@ interface PanelRowCollectionInterface extends \IteratorAggregate * * Note that each panel element decides its name on its own. * - * @return array + * @return list> */ public function getRows(); diff --git a/src/DataDefinition/Definition/View/PanelRowInterface.php b/src/DataDefinition/Definition/View/PanelRowInterface.php index b3a75479..96bfcd1e 100644 --- a/src/DataDefinition/Definition/View/PanelRowInterface.php +++ b/src/DataDefinition/Definition/View/PanelRowInterface.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -25,6 +26,8 @@ /** * This interface describes a panel row definition. + * + * @extends \IteratorAggregate */ interface PanelRowInterface extends \IteratorAggregate { @@ -34,7 +37,7 @@ interface PanelRowInterface extends \IteratorAggregate * This will return the following for example: * array('filter[prop1]', 'filter[prop2]', 'search', 'limit') * - * @return array + * @return list */ public function getElements(); diff --git a/src/DataDefinition/Definition/View/ToggleCommand.php b/src/DataDefinition/Definition/View/ToggleCommand.php index d53452be..fb458564 100644 --- a/src/DataDefinition/Definition/View/ToggleCommand.php +++ b/src/DataDefinition/Definition/View/ToggleCommand.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -31,7 +32,7 @@ class ToggleCommand extends Command implements ToggleCommandInterface * * @var string */ - protected $property; + protected $property = ''; /** * The toggle command is an inverse command. @@ -65,7 +66,8 @@ public function getToggleProperty() */ public function setInverse($inverse) { - $this->inverse = (bool) $inverse; + $this->inverse = $inverse; + return $this; } diff --git a/src/DataDefinition/Definition/View/TranslatedToggleCommand.php b/src/DataDefinition/Definition/View/TranslatedToggleCommand.php index f6cb5df8..fa7737f9 100644 --- a/src/DataDefinition/Definition/View/TranslatedToggleCommand.php +++ b/src/DataDefinition/Definition/View/TranslatedToggleCommand.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,8 @@ * @package contao-community-alliance/dc-general * @author Christian Schiffler * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -30,7 +31,7 @@ class TranslatedToggleCommand extends ToggleCommand implements TranslatedToggleC * * @var string */ - protected $language; + protected $language = ''; /** * {@inheritDoc} diff --git a/src/DataDefinition/ModelRelationship/FilterBuilder.php b/src/DataDefinition/ModelRelationship/FilterBuilder.php index 6b1e0ac7..cba1b797 100644 --- a/src/DataDefinition/ModelRelationship/FilterBuilder.php +++ b/src/DataDefinition/ModelRelationship/FilterBuilder.php @@ -60,13 +60,10 @@ class FilterBuilder */ public function __construct($filter = [], $isRoot = false) { - if (!\is_array($filter)) { - throw new DcGeneralInvalidArgumentException( - 'FilterBuilder needs a valid filter array ' . \gettype($filter) . 'given' - ); - } + $filters = static::getBuilderFromArray(['operation' => 'AND', 'children' => $filter], $this); + assert($filters instanceof AndFilterBuilder); - $this->filters = static::getBuilderFromArray(['operation' => 'AND', 'children' => $filter], $this); + $this->filters = $filters; $this->isRootFilter = $isRoot; } @@ -114,6 +111,7 @@ public static function getBuilderFromArray($filter, $builder) */ public static function fromArray($filter = []) { + /** @psalm-suppress UnsafeInstantiation */ return new static($filter, false); } @@ -126,6 +124,7 @@ public static function fromArray($filter = []) */ public static function fromArrayForRoot($filter = []) { + /** @psalm-suppress UnsafeInstantiation */ return new static($filter, true); } diff --git a/src/DataDefinition/ModelRelationship/FilterBuilder/AndFilterBuilder.php b/src/DataDefinition/ModelRelationship/FilterBuilder/AndFilterBuilder.php index 260b04b5..6fa2c56f 100644 --- a/src/DataDefinition/ModelRelationship/FilterBuilder/AndFilterBuilder.php +++ b/src/DataDefinition/ModelRelationship/FilterBuilder/AndFilterBuilder.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,8 @@ * @package contao-community-alliance/dc-general * @author Christian Schiffler * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -33,7 +34,7 @@ class AndFilterBuilder extends FilterBuilderWithChildren /** * Create a new instance. * - * @param array $children The initial children to absorb. + * @param list $children The initial children to absorb. * * @throws DcGeneralInvalidArgumentException When invalid children have been passed. */ @@ -58,7 +59,7 @@ public function append($filters) } if ($filters instanceof AndFilterBuilder) { - foreach (clone $filters as $filter) { + foreach ($filters as $filter) { $this->add(clone $filter); } diff --git a/src/DataDefinition/ModelRelationship/FilterBuilder/BaseComparingFilterBuilder.php b/src/DataDefinition/ModelRelationship/FilterBuilder/BaseComparingFilterBuilder.php index 2472c7a5..24fb91a0 100644 --- a/src/DataDefinition/ModelRelationship/FilterBuilder/BaseComparingFilterBuilder.php +++ b/src/DataDefinition/ModelRelationship/FilterBuilder/BaseComparingFilterBuilder.php @@ -35,42 +35,35 @@ class BaseComparingFilterBuilder extends BaseFilterBuilder * * @var string */ - protected $operation; + protected $operation = ''; /** * The property to be checked. * * @var string */ - protected $property; - - /** - * The property to be checked. - * - * @var string - */ - protected $remoteProperty; + protected $property = ''; /** * The value to compare against. * * @var mixed */ - protected $value; + protected $value = ''; /** * Flag determining if the passed value is a remote property name or not. * * @var bool */ - protected $isRemote; + protected $isRemote = false; /** * Flag determining if the remote value is a property or literal value. * * @var bool */ - protected $isRemoteProp; + protected $isRemoteProp = false; /** * Create a new instance. @@ -124,6 +117,7 @@ public static function fromArray($array) throw new DcGeneralInvalidArgumentException('Invalid filter array provided ' . \var_export($array, true)); } + /** @psalm-suppress UnsafeInstantiation */ return new static($property, $value, $isRemote, $isRemoteProp); } diff --git a/src/DataDefinition/ModelRelationship/FilterBuilder/BaseFilterBuilder.php b/src/DataDefinition/ModelRelationship/FilterBuilder/BaseFilterBuilder.php index bb6f7763..3575259d 100644 --- a/src/DataDefinition/ModelRelationship/FilterBuilder/BaseFilterBuilder.php +++ b/src/DataDefinition/ModelRelationship/FilterBuilder/BaseFilterBuilder.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Stefan Heimes * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -33,16 +34,16 @@ abstract class BaseFilterBuilder /** * The filter builder holding the scope. * - * @var FilterBuilder + * @var null|FilterBuilder */ - protected $builder; + protected $builder = null; /** - * The current parenting Builder. + * The current parenting builder. * - * @var FilterBuilderWithChildren + * @var null|FilterBuilderWithChildren */ - protected $parent; + protected $parent = null; /** * Get the filter builder. @@ -51,6 +52,10 @@ abstract class BaseFilterBuilder */ public function getBuilder() { + if (null === $this->builder) { + throw new \LogicException('Builder has not been set.'); + } + return $this->builder; } @@ -73,7 +78,7 @@ public function setBuilder($builder) * * @param FilterBuilderWithChildren $parent The new parent. * - * @return FilterBuilderWithChildren + * @return BaseFilterBuilder */ public function setParent(FilterBuilderWithChildren $parent) { @@ -94,6 +99,10 @@ public function setParent(FilterBuilderWithChildren $parent) */ public function getParent() { + if (null === $this->parent) { + throw new \LogicException('Parent has not been set.'); + } + return $this->parent; } @@ -111,7 +120,10 @@ abstract public function get(); */ public function getAllAsArray() { - return $this->builder->getAllAsArray(); + $builder = $this->builder; + assert($builder instanceof FilterBuilder); + + return $builder->getAllAsArray(); } /** @@ -121,12 +133,11 @@ public function getAllAsArray() */ protected function ensureAndEncapsulation() { - $parent = $this->getParent(); - if ($this instanceof AndFilterBuilder) { return $this; } + $parent = $this->getParent(); if (($parent instanceof AndFilterBuilder) && !($this instanceof FilterBuilderWithChildren)) { return $parent; } @@ -227,7 +238,7 @@ public function up() * @param string $property The property name. * @param mixed $value The property value. * - * @return PropertyEqualsFilterBuilder The newly created filter. + * @return BaseFilterBuilder The newly created filter. */ public function andPropertyEquals($property, $value) { @@ -240,7 +251,7 @@ public function andPropertyEquals($property, $value) * @param string $property The property name. * @param mixed $value The property value. * - * @return PropertyEqualsFilterBuilder The newly created filter. + * @return BaseFilterBuilder The newly created filter. */ public function orPropertyEquals($property, $value) { @@ -254,7 +265,7 @@ public function orPropertyEquals($property, $value) * @param string $remoteProperty The name of the remote property. * @param bool $remoteIsValue True if the passed remote value is a value, false if it is a property name. * - * @return PropertyEqualsFilterBuilder The newly created filter. + * @return BaseFilterBuilder The newly created filter. */ public function andRemotePropertyEquals($property, $remoteProperty, $remoteIsValue = false) { @@ -271,7 +282,7 @@ public function andRemotePropertyEquals($property, $remoteProperty, $remoteIsVal * @param string $property The property name. * @param mixed $value The property value. * - * @return PropertyGreaterThanFilterBuilder The newly created filter. + * @return BaseFilterBuilder The newly created filter. */ public function andPropertyGreaterThan($property, $value) { @@ -285,7 +296,7 @@ public function andPropertyGreaterThan($property, $value) * @param string $remoteProperty The name of the remote property. * @param bool $remoteIsValue True if the passed remote value is a value, false if it is a property name. * - * @return PropertyGreaterThanFilterBuilder The newly created filter. + * @return BaseFilterBuilder The newly created filter. */ public function andRemotePropertyGreaterThan($property, $remoteProperty, $remoteIsValue = false) { @@ -302,7 +313,7 @@ public function andRemotePropertyGreaterThan($property, $remoteProperty, $remote * @param string $property The property name. * @param mixed $value The property value. * - * @return PropertyLessThanFilterBuilder The newly created filter. + * @return BaseFilterBuilder The newly created filter. */ public function andPropertyLessThan($property, $value) { @@ -316,7 +327,7 @@ public function andPropertyLessThan($property, $value) * @param string $remoteProperty The name of the remote property. * @param bool $remoteIsValue True if the passed remote value is a value, false if it is a property name. * - * @return PropertyLessThanFilterBuilder The newly created filter. + * @return BaseFilterBuilder The newly created filter. */ public function andRemotePropertyLessThan($property, $remoteProperty, $remoteIsValue = false) { @@ -333,7 +344,7 @@ public function andRemotePropertyLessThan($property, $remoteProperty, $remoteIsV * @param string $property The property name. * @param mixed $value The property value. * - * @return PropertyValueInFilterBuilder The newly created filter. + * @return BaseFilterBuilder The newly created filter. */ public function andPropertyValueIn($property, $value) { @@ -346,7 +357,7 @@ public function andPropertyValueIn($property, $value) * @param string $property The property name. * @param mixed $value The property value. * - * @return PropertyValueInFilterBuilder The newly created filter. + * @return BaseFilterBuilder The newly created filter. */ public function andPropertyValueLike($property, $value) { diff --git a/src/DataDefinition/ModelRelationship/FilterBuilder/FilterBuilderWithChildren.php b/src/DataDefinition/ModelRelationship/FilterBuilder/FilterBuilderWithChildren.php index 47d364ec..ca0bd63f 100644 --- a/src/DataDefinition/ModelRelationship/FilterBuilder/FilterBuilderWithChildren.php +++ b/src/DataDefinition/ModelRelationship/FilterBuilder/FilterBuilderWithChildren.php @@ -21,30 +21,38 @@ namespace ContaoCommunityAlliance\DcGeneral\DataDefinition\ModelRelationship\FilterBuilder; +use ArrayAccess; use ContaoCommunityAlliance\DcGeneral\DataDefinition\ModelRelationship\FilterBuilder; use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralInvalidArgumentException; use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralRuntimeException; +use Iterator; +use ReturnTypeWillChange; + +use function count; /** * Handy helper class to generate and manipulate filter arrays containing children. * * This class is intended to be only used as base class of other filters and via the FilterBuilder main class. * + * @implements Iterator + * @implements ArrayAccess + * * @SuppressWarnings(PHPMD.TooManyPublicMethods) We have to keep them as we implement the interfaces. */ -class FilterBuilderWithChildren extends BaseFilterBuilder implements \Iterator, \ArrayAccess +class FilterBuilderWithChildren extends BaseFilterBuilder implements Iterator, ArrayAccess { /** * The operation string. * * @var string */ - protected $operation; + protected $operation = ''; /** * The children within this filter builder. * - * @var BaseFilterBuilder[] + * @var array */ protected $children; @@ -58,18 +66,12 @@ class FilterBuilderWithChildren extends BaseFilterBuilder implements \Iterator, /** * Create a new instance. * - * @param array $children The initial children to absorb. + * @param list $children The initial children to absorb. * * @throws DcGeneralInvalidArgumentException When invalid children have been passed. */ public function __construct($children = []) { - if (!\is_array($children)) { - throw new DcGeneralInvalidArgumentException( - __CLASS__ . ' needs a valid child filter array ' . \gettype($children) . 'given' - ); - } - $this->children = []; foreach ($children as $child) { @@ -79,7 +81,7 @@ public function __construct($children = []) } /** - * {@inheritdoc} + * Clone the object. */ public function __clone() { @@ -98,10 +100,10 @@ public function __clone() * * @throws DcGeneralRuntimeException When the current position is invalid. */ - public function current(): ?BaseFilterBuilder + public function current(): BaseFilterBuilder { if (-1 === $this->index) { - return $this->first(); + $this->first(); } if (!$this->valid()) { @@ -114,8 +116,11 @@ public function current(): ?BaseFilterBuilder /** * Move forward to next element and return it. * - * @return BaseFilterBuilder + * @return BaseFilterBuilder|null + * + * @psalm-suppress ImplementedReturnTypeMismatch */ + #[ReturnTypeWillChange] public function next(): ?BaseFilterBuilder { $this->index++; @@ -126,9 +131,9 @@ public function next(): ?BaseFilterBuilder /** * Return the key of the current element. * - * @return mixed scalar on success, or null on failure. + * @return int scalar on success, or null on failure. */ - public function key(): mixed + public function key(): int { return $this->index; } @@ -140,7 +145,7 @@ public function key(): mixed */ public function valid(): bool { - return ($this->index > -1) && ($this->index < \count($this->children)); + return ($this->index > -1) && ($this->index < count($this->children)); } /** @@ -148,8 +153,11 @@ public function valid(): bool * * This is an alias for {@link FilterBuilderWithChildren::first()} only present for implementing Iterator interface. * - * @return BaseFilterBuilder + * @return BaseFilterBuilder|null + * + * @psalm-suppress ImplementedReturnTypeMismatch */ + #[ReturnTypeWillChange] public function rewind(): ?BaseFilterBuilder { return $this->first(); @@ -158,11 +166,11 @@ public function rewind(): ?BaseFilterBuilder /** * Rewind the Iterator to the first element and return it. * - * @return BaseFilterBuilder + * @return BaseFilterBuilder|null */ public function first() { - $this->index = \count($this->children) ? 0 : (-1); + $this->index = count($this->children) ? 0 : (-1); if ($this->index === -1) { return null; @@ -202,7 +210,10 @@ public function offsetGet($offset): BaseFilterBuilder * @param mixed $value The value to set. * * @return FilterBuilderWithChildren The current builder. + * + * @psalm-suppress ImplementedReturnTypeMismatch */ + #[ReturnTypeWillChange] public function offsetSet($offset, $value): FilterBuilderWithChildren { $this->children[$offset] = $value; @@ -216,7 +227,10 @@ public function offsetSet($offset, $value): FilterBuilderWithChildren * @param int $offset The offset to unset. * * @return FilterBuilderWithChildren The current builder. + * + * @psalm-suppress ImplementedReturnTypeMismatch */ + #[ReturnTypeWillChange] public function offsetUnset($offset): FilterBuilderWithChildren { unset($this->children[$offset]); @@ -269,7 +283,10 @@ public function add($filter) if ($index === -1) { $this->children[] = $filter; - $filter->setBuilder($this->builder)->setParent($this); + $filter->setParent($this); + if (null !== $this->builder) { + $filter->setBuilder($this->builder); + } } return $this; @@ -308,6 +325,7 @@ public static function fromArray($array, $builder) $children[] = FilterBuilder::getBuilderFromArray($child, $builder); } + /** @psalm-suppress UnsafeInstantiation */ return (new static($children))->setBuilder($builder); } diff --git a/src/DataDefinition/ModelRelationship/FilterBuilder/OrFilterBuilder.php b/src/DataDefinition/ModelRelationship/FilterBuilder/OrFilterBuilder.php index de427cd7..0a2940f4 100644 --- a/src/DataDefinition/ModelRelationship/FilterBuilder/OrFilterBuilder.php +++ b/src/DataDefinition/ModelRelationship/FilterBuilder/OrFilterBuilder.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,8 @@ * @package contao-community-alliance/dc-general * @author Christian Schiffler * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -32,7 +33,7 @@ class OrFilterBuilder extends FilterBuilderWithChildren /** * Create a new instance. * - * @param array $children The initial children to absorb. + * @param list $children The initial children to absorb. * * @throws DcGeneralInvalidArgumentException When invalid children have been passed. */ diff --git a/src/DataDefinition/ModelRelationship/FilterBuilder/PropertyValueInFilterBuilder.php b/src/DataDefinition/ModelRelationship/FilterBuilder/PropertyValueInFilterBuilder.php index 4233b42e..fd3341b1 100644 --- a/src/DataDefinition/ModelRelationship/FilterBuilder/PropertyValueInFilterBuilder.php +++ b/src/DataDefinition/ModelRelationship/FilterBuilder/PropertyValueInFilterBuilder.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,8 @@ * @package contao-community-alliance/dc-general * @author Christian Schiffler * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -51,7 +52,6 @@ class PropertyValueInFilterBuilder extends BaseFilterBuilder */ public function __construct($property, $values) { - $this->operation = 'IN'; $this->setProperty($property)->setValues($values); } @@ -60,7 +60,7 @@ public function __construct($property, $values) * * @param array $array The initialization array. * - * @return mixed + * @return static * * @throws DcGeneralInvalidArgumentException When an invalid array has been passed. */ @@ -73,6 +73,7 @@ public static function fromArray($array) throw new DcGeneralInvalidArgumentException('Invalid filter array provided ' . \var_export($array, true)); } + /** @psalm-suppress UnsafeInstantiation */ return new static($property, $values); } diff --git a/src/DataDefinition/ModelRelationship/ParentChildCondition.php b/src/DataDefinition/ModelRelationship/ParentChildCondition.php index 0acec112..72ddd84f 100644 --- a/src/DataDefinition/ModelRelationship/ParentChildCondition.php +++ b/src/DataDefinition/ModelRelationship/ParentChildCondition.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2021 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -16,7 +16,8 @@ * @author Tristan Lins * @author Sven Baumann * @author David Molineus - * @copyright 2013-2021 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -46,43 +47,43 @@ class ParentChildCondition extends AbstractCondition implements ParentChildCondi * * @var array */ - protected $inverseFilter; + protected $inverseFilter = []; /** * The values to use when enforcing a root condition. * * @var array */ - protected $setOn; + protected $setOn = []; /** * The name of the source provider (parent). * * @var string */ - protected $sourceProvider; + protected $sourceProvider = ''; /** * The name of the destination provider (child). * * @var string */ - protected $destinationProvider; + protected $destinationProvider = ''; /** * Local cache property for the needed properties for filtering. * - * @var array + * @var list|null */ - private $neededProperties; + private $neededProperties = null; /** * {@inheritdoc} */ public function setFilterArray($value) { - $this->filter = (array) $value; - unset($this->neededProperties); + $this->filter = $value; + $this->neededProperties = null; return $this; } @@ -100,7 +101,7 @@ public function getFilterArray() */ public function setSetters($value) { - $this->setOn = (array) $value; + $this->setOn = $value; return $this; } @@ -118,12 +119,8 @@ public function getSetters() */ public function setInverseFilterArray($value) { - if (empty($value)) { - $this->inverseFilter = null; - return $this; - } + $this->inverseFilter = $value; - $this->inverseFilter = (array) $value; return $this; } @@ -132,7 +129,7 @@ public function setInverseFilterArray($value) */ public function getInverseFilterArray() { - return ($this->inverseFilter ?? []); + return $this->inverseFilter; } /** @@ -140,7 +137,7 @@ public function getInverseFilterArray() */ public function setSourceName($value) { - $this->sourceProvider = (string) $value; + $this->sourceProvider = $value; return $this; } @@ -158,7 +155,7 @@ public function getSourceName() */ public function setDestinationName($value) { - $this->destinationProvider = (string) $value; + $this->destinationProvider = $value; return $this; } @@ -219,10 +216,6 @@ public function parseFilter($filter, $model) */ public function getFilter($parent) { - if (!$parent) { - throw new DcGeneralInvalidArgumentException('No parent model passed.'); - } - $result = []; foreach ($this->getFilterArray() as $child) { $result[] = $this->parseFilter($child, $parent); @@ -234,7 +227,7 @@ public function getFilter($parent) /** * Check if the passed value is a valid setter. * - * @param array $setter The setter. + * @param mixed $setter The setter. * * @return bool */ @@ -257,7 +250,7 @@ public function applyTo($objParent, $objChild) $setters = $this->getSetters(); - if (empty($setters) || !\is_array($setters)) { + if (empty($setters)) { throw new DcGeneralRuntimeException( \sprintf( 'No relationship setter defined from %s to %s.', @@ -300,7 +293,7 @@ public function copyFrom($sourceModel, $destinationModel) $setters = $this->getSetters(); - if (empty($setters) || !\is_array($setters)) { + if (empty($setters)) { throw new DcGeneralRuntimeException( \sprintf( 'No relationship setter defined from %s to %s.', @@ -372,7 +365,7 @@ public function getInverseFilterFor($child) * @param array $rule The rule to prepare. * @param ModelInterface $child The child to be checked. * - * @return array. + * @return array */ protected function prepareRule($rule, $child) { @@ -436,28 +429,31 @@ public function matches($objParent, $objChild) * * @param array $rule The filter rule from which the properties shall be extracted from. * - * @return array + * @return list * * @throws \RuntimeException When an unexpected filter rule is encountered. */ private function extractNeededProperties($rule) { if (\in_array($rule['operation'], ['AND', 'OR'])) { - $properties = []; - foreach ($rule['children'] as $childRule) { + $properties = [[]]; + foreach ($rule['children'] ?? [] as $childRule) { $properties[] = $this->extractNeededProperties($childRule); } - return $properties; + + return \array_merge(...$properties); } // Local is child property name. if (isset($rule['local'])) { - return $rule['local']; + /** @var array{local: string} $rule */ + return [$rule['local']]; } // Remote is parent property name. if (isset($rule['property'])) { - return $rule['property']; + /** @var array{property: string} $rule */ + return [$rule['property']]; } throw new \RuntimeException('Unexpected filter rule ' . \var_export($rule, true)); @@ -468,7 +464,7 @@ private function extractNeededProperties($rule) */ public function neededProperties() { - if (!isset($this->neededProperties)) { + if (null === $this->neededProperties) { $this->neededProperties = $this->extractNeededProperties( [ 'operation' => 'AND', diff --git a/src/DataDefinition/ModelRelationship/ParentChildConditionInterface.php b/src/DataDefinition/ModelRelationship/ParentChildConditionInterface.php index d3d48538..62c07fc0 100644 --- a/src/DataDefinition/ModelRelationship/ParentChildConditionInterface.php +++ b/src/DataDefinition/ModelRelationship/ParentChildConditionInterface.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -79,11 +80,11 @@ public function getInverseFilterArray(); /** * Get the condition as filter related to the given model. * - * @param ModelInterface $objParent The model that shall get used as parent. + * @param ModelInterface $parent The model that shall get used as parent. * * @return array */ - public function getFilter($objParent); + public function getFilter($parent); /** * Set the name of the source provider. @@ -142,12 +143,12 @@ public function copyFrom($sourceModel, $destinationModel); * * This allows to look up the parent of a child model. * - * @param ModelInterface $objChild The model that shall get used as child and for which the parent filter shall get - * retrieved. + * @param ModelInterface $child The model that shall get used as child and for which the parent filter shall get + * retrieved. * * @return array|null */ - public function getInverseFilterFor($objChild); + public function getInverseFilterFor($child); /** * Test if the given parent is indeed a parent of the given child object for this condition. @@ -162,7 +163,7 @@ public function matches($objParent, $objChild); /** * Return the names of the needed properties for filtering. * - * @return array + * @return list */ public function neededProperties(); } diff --git a/src/DataDefinition/ModelRelationship/RootCondition.php b/src/DataDefinition/ModelRelationship/RootCondition.php index 46fe8459..1e8df211 100644 --- a/src/DataDefinition/ModelRelationship/RootCondition.php +++ b/src/DataDefinition/ModelRelationship/RootCondition.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Sven Baumann * @author Richard Henkenjohann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -34,7 +35,7 @@ class RootCondition extends AbstractCondition implements RootConditionInterface * * @var array */ - protected $filter; + protected $filter = []; /** * The setter information to use when a model shall get marked as root item. @@ -48,7 +49,7 @@ class RootCondition extends AbstractCondition implements RootConditionInterface * * @var string */ - protected $sourceProvider; + protected $sourceProvider = ''; /** * {@inheritdoc} @@ -140,17 +141,17 @@ public function applyTo($model) /** * {@inheritdoc} */ - public function matches($model) + public function matches($objModel) { try { - $this->guardProviderName($model); + $this->guardProviderName($objModel); } catch (\InvalidArgumentException $exception) { return false; } if ($this->getFilterArray()) { return static::checkCondition( - $model, + $objModel, [ 'operation' => 'AND', 'children' => $this->getFilterArray() diff --git a/src/DataDefinition/ModelRelationship/RootConditionInterface.php b/src/DataDefinition/ModelRelationship/RootConditionInterface.php index 1aedace3..67c07fc2 100644 --- a/src/DataDefinition/ModelRelationship/RootConditionInterface.php +++ b/src/DataDefinition/ModelRelationship/RootConditionInterface.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,7 @@ * @package contao-community-alliance/dc-general * @author Christian Schiffler * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -78,11 +78,11 @@ public function getSourceName(); /** * Apply the root condition to a model. * - * @param ModelInterface $objModel The model that shall become a root model. + * @param ModelInterface $model The model that shall become a root model. * * @return RootConditionInterface */ - public function applyTo($objModel); + public function applyTo($model); /** * Test if the given model is indeed a root object according to this condition. diff --git a/src/DataDefinition/Palette/Builder/Event/AddConditionEvent.php b/src/DataDefinition/Palette/Builder/Event/AddConditionEvent.php index 28509af2..338eca0d 100644 --- a/src/DataDefinition/Palette/Builder/Event/AddConditionEvent.php +++ b/src/DataDefinition/Palette/Builder/Event/AddConditionEvent.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -30,6 +31,9 @@ /** * This event is emitted when an condition is added by the palette builder. + * + * @template TCondition of PaletteConditionInterface|PropertyConditionInterface + * @template TTarget of PaletteInterface|PropertyInterface */ class AddConditionEvent extends BuilderEvent { @@ -38,23 +42,23 @@ class AddConditionEvent extends BuilderEvent /** * The condition that is being added. * - * @var PaletteConditionInterface|PropertyConditionInterface + * @var TCondition */ protected $condition; /** * The target to which the condition is being added. * - * @var PaletteInterface|PropertyInterface + * @var TTarget */ protected $target; /** * Create a new instance. * - * @param PaletteConditionInterface|PropertyConditionInterface $condition The condition being added. - * @param PaletteInterface|PropertyInterface $target The target property or palette. - * @param PaletteBuilder $paletteBuilder The palette builder in use. + * @param TCondition $condition The condition being added. + * @param TTarget $target The target property or palette. + * @param PaletteBuilder $paletteBuilder The palette builder in use. */ public function __construct($condition, $target, PaletteBuilder $paletteBuilder) { @@ -66,7 +70,7 @@ public function __construct($condition, $target, PaletteBuilder $paletteBuilder) /** * Set the condition. * - * @param PaletteConditionInterface|PropertyConditionInterface $condition The condition. + * @param TCondition $condition The condition. * * @return AddConditionEvent * @@ -91,7 +95,7 @@ public function setCondition($condition) /** * Retrieve the condition. * - * @return PaletteConditionInterface|PropertyConditionInterface + * @return TCondition */ public function getCondition() { @@ -101,7 +105,7 @@ public function getCondition() /** * Set the target. * - * @param PaletteInterface|PropertyInterface $target The target property or palette. + * @param TTarget $target The target property or palette. * * @return AddConditionEvent * @@ -120,7 +124,7 @@ public function setTarget($target) /** * Retrieve the target. * - * @return PaletteInterface|PropertyInterface + * @return TTarget */ public function getTarget() { diff --git a/src/DataDefinition/Palette/Builder/Event/BuilderEvent.php b/src/DataDefinition/Palette/Builder/Event/BuilderEvent.php index de13da7e..bbaee38a 100644 --- a/src/DataDefinition/Palette/Builder/Event/BuilderEvent.php +++ b/src/DataDefinition/Palette/Builder/Event/BuilderEvent.php @@ -32,6 +32,9 @@ */ abstract class BuilderEvent extends AbstractContainerAwareEvent { + /** + * @var string + */ public const NAME = 'dc-general.data-definition.palette.builder.builder'; /** @@ -48,7 +51,7 @@ abstract class BuilderEvent extends AbstractContainerAwareEvent */ public function __construct(PaletteBuilder $paletteBuilder) { - parent::__construct($this->paletteBuilder->getContainer()); + parent::__construct($paletteBuilder->getContainer()); $this->paletteBuilder = $paletteBuilder; } diff --git a/src/DataDefinition/Palette/Builder/Event/CreateConditionEvent.php b/src/DataDefinition/Palette/Builder/Event/CreateConditionEvent.php index 0c2b5ef0..a4a4175c 100644 --- a/src/DataDefinition/Palette/Builder/Event/CreateConditionEvent.php +++ b/src/DataDefinition/Palette/Builder/Event/CreateConditionEvent.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -28,6 +29,8 @@ /** * This event gets emitted when a condition is created. + * + * @template TCondition of PaletteConditionInterface|PropertyConditionInterface */ class CreateConditionEvent extends BuilderEvent { @@ -36,16 +39,16 @@ class CreateConditionEvent extends BuilderEvent /** * The condition being created. * - * @var PaletteConditionInterface|PropertyConditionInterface + * @var TCondition */ protected $condition; /** * Create a new instance. * - * @param PaletteConditionInterface|PropertyConditionInterface $condition The condition that has been created. - * @param PaletteBuilder $paletteBuilder The palette builder that created the - * condition. + * @param TCondition $condition The condition that has been created. + * @param PaletteBuilder $paletteBuilder The palette builder that created the + * condition. */ public function __construct($condition, PaletteBuilder $paletteBuilder) { @@ -57,7 +60,7 @@ public function __construct($condition, PaletteBuilder $paletteBuilder) /** * Set the condition. * - * @param PaletteConditionInterface|PropertyConditionInterface $condition The condition to use. + * @param TCondition $condition The condition to use. * * @return CreateConditionEvent * @@ -83,7 +86,7 @@ public function setCondition($condition) /** * Retrieve the condition. * - * @return PaletteConditionInterface|PropertyConditionInterface + * @return TCondition */ public function getCondition() { diff --git a/src/DataDefinition/Palette/Builder/Event/CreateDefaultPaletteConditionEvent.php b/src/DataDefinition/Palette/Builder/Event/CreateDefaultPaletteConditionEvent.php index 08b47943..275ba3fc 100644 --- a/src/DataDefinition/Palette/Builder/Event/CreateDefaultPaletteConditionEvent.php +++ b/src/DataDefinition/Palette/Builder/Event/CreateDefaultPaletteConditionEvent.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -22,7 +23,7 @@ namespace ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Builder\Event; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Builder\PaletteBuilder; -use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Condition\Palette\DefaultPaletteCondition; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Condition\Palette\PaletteConditionInterface; /** * This event gets emitted when a condition for the default palette is created. @@ -34,17 +35,17 @@ class CreateDefaultPaletteConditionEvent extends BuilderEvent /** * The default palette condition. * - * @var DefaultPaletteCondition + * @var PaletteConditionInterface */ protected $paletteCondition; /** * Create a new instance. * - * @param DefaultPaletteCondition $paletteCondition The condition that has been created. - * @param PaletteBuilder $paletteBuilder The palette builder creating the condition. + * @param PaletteConditionInterface $paletteCondition The condition that has been created. + * @param PaletteBuilder $paletteBuilder The palette builder creating the condition. */ - public function __construct(DefaultPaletteCondition $paletteCondition, PaletteBuilder $paletteBuilder) + public function __construct(PaletteConditionInterface $paletteCondition, PaletteBuilder $paletteBuilder) { $this->setDefaultPaletteCondition($paletteCondition); parent::__construct($paletteBuilder); @@ -53,11 +54,11 @@ public function __construct(DefaultPaletteCondition $paletteCondition, PaletteBu /** * Set the condition. * - * @param DefaultPaletteCondition $paletteCondition The condition. + * @param PaletteConditionInterface $paletteCondition The condition. * * @return CreateDefaultPaletteConditionEvent */ - public function setDefaultPaletteCondition(DefaultPaletteCondition $paletteCondition) + public function setDefaultPaletteCondition(PaletteConditionInterface $paletteCondition) { $this->paletteCondition = $paletteCondition; @@ -67,7 +68,7 @@ public function setDefaultPaletteCondition(DefaultPaletteCondition $paletteCondi /** * Retrieve the condition. * - * @return DefaultPaletteCondition + * @return PaletteConditionInterface */ public function getDefaultPaletteCondition() { diff --git a/src/DataDefinition/Palette/Builder/Event/CreatePaletteConditionChainEvent.php b/src/DataDefinition/Palette/Builder/Event/CreatePaletteConditionChainEvent.php index 0626ff95..5b50a4f3 100644 --- a/src/DataDefinition/Palette/Builder/Event/CreatePaletteConditionChainEvent.php +++ b/src/DataDefinition/Palette/Builder/Event/CreatePaletteConditionChainEvent.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,15 +14,17 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ namespace ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Builder\Event; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ConditionChainInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Builder\PaletteBuilder; -use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Condition\Palette\PaletteConditionChain; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Condition\Palette\PaletteConditionInterface; /** * This event gets emitted when a palette condition chain is created. @@ -34,18 +36,20 @@ class CreatePaletteConditionChainEvent extends BuilderEvent /** * The palette condition chain being created. * - * @var PaletteConditionChain + * @var ConditionChainInterface&PaletteConditionInterface */ protected $conditionChain; /** * Create a new instance. * - * @param PaletteConditionChain $conditionChain The palette condition chain. - * @param PaletteBuilder $paletteBuilder The palette builder in use. + * @param ConditionChainInterface&PaletteConditionInterface $conditionChain The palette condition chain. + * @param PaletteBuilder $paletteBuilder The palette builder in use. */ - public function __construct(PaletteConditionChain $conditionChain, PaletteBuilder $paletteBuilder) - { + public function __construct( + ConditionChainInterface&PaletteConditionInterface $conditionChain, + PaletteBuilder $paletteBuilder + ) { $this->setPaletteConditionChain($conditionChain); parent::__construct($paletteBuilder); } @@ -53,11 +57,11 @@ public function __construct(PaletteConditionChain $conditionChain, PaletteBuilde /** * Set the palette condition chain. * - * @param PaletteConditionChain $conditionChain The condition chain. + * @param ConditionChainInterface&PaletteConditionInterface $conditionChain The condition chain. * * @return CreatePaletteConditionChainEvent */ - public function setPaletteConditionChain(PaletteConditionChain $conditionChain) + public function setPaletteConditionChain(ConditionChainInterface&PaletteConditionInterface $conditionChain) { $this->conditionChain = $conditionChain; @@ -67,7 +71,7 @@ public function setPaletteConditionChain(PaletteConditionChain $conditionChain) /** * Retrieve the palette condition chain. * - * @return PaletteConditionChain + * @return ConditionChainInterface&PaletteConditionInterface */ public function getPaletteConditionChain() { diff --git a/src/DataDefinition/Palette/Builder/Event/CreatePropertyConditionChainEvent.php b/src/DataDefinition/Palette/Builder/Event/CreatePropertyConditionChainEvent.php index 2fdfdac5..41a55041 100644 --- a/src/DataDefinition/Palette/Builder/Event/CreatePropertyConditionChainEvent.php +++ b/src/DataDefinition/Palette/Builder/Event/CreatePropertyConditionChainEvent.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,15 +14,17 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ namespace ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Builder\Event; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ConditionChainInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Builder\PaletteBuilder; -use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Condition\Property\PropertyConditionChain; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Condition\Property\PropertyConditionInterface; /** * This event gets emitted when a property condition chain is created. @@ -34,18 +36,21 @@ class CreatePropertyConditionChainEvent extends BuilderEvent /** * The property condition chain. * - * @var PropertyConditionChain + * @var ConditionChainInterface&PropertyConditionInterface */ protected $conditionChain; /** * Create a new instance. * - * @param PropertyConditionChain $conditionChain The property condition chain that has been created. - * @param PaletteBuilder $paletteBuilder The palette builder in use. + * @param ConditionChainInterface&PropertyConditionInterface $conditionChain The property condition chain that has + * been created. + * @param PaletteBuilder $paletteBuilder The palette builder in use. */ - public function __construct(PropertyConditionChain $conditionChain, PaletteBuilder $paletteBuilder) - { + public function __construct( + ConditionChainInterface&PropertyConditionInterface $conditionChain, + PaletteBuilder $paletteBuilder + ) { $this->setPropertyConditionChain($conditionChain); parent::__construct($paletteBuilder); } @@ -53,11 +58,11 @@ public function __construct(PropertyConditionChain $conditionChain, PaletteBuild /** * Set the property condition chain. * - * @param PropertyConditionChain $conditionChain The property condition chain. + * @param ConditionChainInterface&PropertyConditionInterface $conditionChain The property condition chain. * * @return CreatePropertyConditionChainEvent */ - public function setPropertyConditionChain(PropertyConditionChain $conditionChain) + public function setPropertyConditionChain(ConditionChainInterface&PropertyConditionInterface $conditionChain) { $this->conditionChain = $conditionChain; return $this; @@ -66,7 +71,7 @@ public function setPropertyConditionChain(PropertyConditionChain $conditionChain /** * Retrieve the property condition chain. * - * @return PropertyConditionChain + * @return ConditionChainInterface&PropertyConditionInterface */ public function getPropertyConditionChain() { diff --git a/src/DataDefinition/Palette/Builder/Event/SetDefaultPaletteConditionClassNameEvent.php b/src/DataDefinition/Palette/Builder/Event/SetDefaultPaletteConditionClassNameEvent.php index 6291f5d9..2bddd9e3 100644 --- a/src/DataDefinition/Palette/Builder/Event/SetDefaultPaletteConditionClassNameEvent.php +++ b/src/DataDefinition/Palette/Builder/Event/SetDefaultPaletteConditionClassNameEvent.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -22,6 +23,7 @@ namespace ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Builder\Event; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Builder\PaletteBuilder; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Condition\Palette\PaletteConditionInterface; /** * This event gets emitted when the class name of the default palette condition is set. @@ -33,15 +35,15 @@ class SetDefaultPaletteConditionClassNameEvent extends BuilderEvent /** * The class name. * - * @var string + * @var class-string */ protected $className; /** * Create a new instance. * - * @param string $className The class name. - * @param PaletteBuilder $paletteBuilder The palette builder in use. + * @param class-string $className The class name. + * @param PaletteBuilder $paletteBuilder The palette builder in use. */ public function __construct($className, PaletteBuilder $paletteBuilder) { @@ -52,20 +54,20 @@ public function __construct($className, PaletteBuilder $paletteBuilder) /** * Set the class name. * - * @param string $className The class name. + * @param class-string $className The class name. * * @return SetDefaultPaletteConditionClassNameEvent */ public function setDefaultPaletteConditionClassName($className) { - $this->className = (string) $className; + $this->className = $className; return $this; } /** * Retrieve the class name. * - * @return string + * @return class-string */ public function getDefaultPaletteConditionClassName() { diff --git a/src/DataDefinition/Palette/Builder/Event/SetLegendClassNameEvent.php b/src/DataDefinition/Palette/Builder/Event/SetLegendClassNameEvent.php index b18d3c53..520216da 100644 --- a/src/DataDefinition/Palette/Builder/Event/SetLegendClassNameEvent.php +++ b/src/DataDefinition/Palette/Builder/Event/SetLegendClassNameEvent.php @@ -22,6 +22,7 @@ namespace ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Builder\Event; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Builder\PaletteBuilder; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\LegendInterface; /** * This event gets emitted when a legend class name is set. @@ -33,15 +34,15 @@ class SetLegendClassNameEvent extends BuilderEvent /** * The class name. * - * @var string + * @var class-string */ protected $legendClassName; /** * Create a new instance. * - * @param string $legendClassName The class name. - * @param PaletteBuilder $paletteBuilder The palette builder in use. + * @param class-string $legendClassName The class name. + * @param PaletteBuilder $paletteBuilder The palette builder in use. */ public function __construct($legendClassName, PaletteBuilder $paletteBuilder) { @@ -52,13 +53,13 @@ public function __construct($legendClassName, PaletteBuilder $paletteBuilder) /** * Set the legend class name. * - * @param string $legendClassName The class name. + * @param class-string $legendClassName The class name. * * @return SetLegendClassNameEvent */ public function setLegendClassName($legendClassName) { - $this->legendClassName = (string) $legendClassName; + $this->legendClassName = $legendClassName; return $this; } @@ -66,7 +67,7 @@ public function setLegendClassName($legendClassName) /** * Retrieve the class name. * - * @return string + * @return class-string */ public function getLegendClassName() { diff --git a/src/DataDefinition/Palette/Builder/Event/SetPaletteClassNameEvent.php b/src/DataDefinition/Palette/Builder/Event/SetPaletteClassNameEvent.php index 01fd54dd..26caf6aa 100644 --- a/src/DataDefinition/Palette/Builder/Event/SetPaletteClassNameEvent.php +++ b/src/DataDefinition/Palette/Builder/Event/SetPaletteClassNameEvent.php @@ -22,6 +22,7 @@ namespace ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Builder\Event; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Builder\PaletteBuilder; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\PaletteInterface; /** * This event gets emitted when a palette class name is set. @@ -33,15 +34,15 @@ class SetPaletteClassNameEvent extends BuilderEvent /** * The palette class name. * - * @var string + * @var class-string */ protected $paletteClassName; /** * Create a new instance. * - * @param string $paletteClassName The class name. - * @param PaletteBuilder $paletteBuilder The palette builder in use. + * @param class-string $paletteClassName The class name. + * @param PaletteBuilder $paletteBuilder The palette builder in use. */ public function __construct($paletteClassName, PaletteBuilder $paletteBuilder) { @@ -52,13 +53,13 @@ public function __construct($paletteClassName, PaletteBuilder $paletteBuilder) /** * Set the class name. * - * @param string $paletteClassName The class name. + * @param class-string $paletteClassName The class name. * * @return SetPaletteClassNameEvent */ public function setPaletteClassName($paletteClassName) { - $this->paletteClassName = (string) $paletteClassName; + $this->paletteClassName = $paletteClassName; return $this; } @@ -66,7 +67,7 @@ public function setPaletteClassName($paletteClassName) /** * Retrieve the class name. * - * @return string + * @return class-string */ public function getPaletteClassName() { diff --git a/src/DataDefinition/Palette/Builder/Event/SetPaletteCollectionClassNameEvent.php b/src/DataDefinition/Palette/Builder/Event/SetPaletteCollectionClassNameEvent.php index 9fe476fe..ddc9e6cf 100644 --- a/src/DataDefinition/Palette/Builder/Event/SetPaletteCollectionClassNameEvent.php +++ b/src/DataDefinition/Palette/Builder/Event/SetPaletteCollectionClassNameEvent.php @@ -22,6 +22,7 @@ namespace ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Builder\Event; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Builder\PaletteBuilder; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\PaletteCollectionInterface; /** * This event gets emitted when a palette collection class name is set. @@ -33,15 +34,15 @@ class SetPaletteCollectionClassNameEvent extends BuilderEvent /** * The palette collection class name. * - * @var string + * @var class-string */ protected $className; /** * Create a new instance. * - * @param string $className The class name. - * @param PaletteBuilder $paletteBuilder The palette builder. + * @param class-string $className The class name. + * @param PaletteBuilder $paletteBuilder The palette builder. */ public function __construct($className, PaletteBuilder $paletteBuilder) { @@ -52,13 +53,13 @@ public function __construct($className, PaletteBuilder $paletteBuilder) /** * Set the class name. * - * @param string $className The class name. + * @param class-string $className The class name. * * @return SetPaletteCollectionClassNameEvent */ public function setPaletteCollectionClassName($className) { - $this->className = (string) $className; + $this->className = $className; return $this; } @@ -66,7 +67,7 @@ public function setPaletteCollectionClassName($className) /** * Retrieve the class name. * - * @return string + * @return class-string */ public function getPaletteCollectionClassName() { diff --git a/src/DataDefinition/Palette/Builder/Event/SetPaletteConditionChainClassNameEvent.php b/src/DataDefinition/Palette/Builder/Event/SetPaletteConditionChainClassNameEvent.php index f8afede7..7fa6c02c 100644 --- a/src/DataDefinition/Palette/Builder/Event/SetPaletteConditionChainClassNameEvent.php +++ b/src/DataDefinition/Palette/Builder/Event/SetPaletteConditionChainClassNameEvent.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,17 +14,22 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ namespace ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Builder\Event; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ConditionChainInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Builder\PaletteBuilder; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Condition\Palette\PaletteConditionInterface; /** * This event gets emitted when a palette condition chain class name is set. + * + * @psalm-type TConditionInterface=ConditionChainInterface&PaletteConditionInterface */ class SetPaletteConditionChainClassNameEvent extends BuilderEvent { @@ -33,15 +38,15 @@ class SetPaletteConditionChainClassNameEvent extends BuilderEvent /** * The class name. * - * @var string + * @var class-string */ protected $className; /** * Create a new instance. * - * @param string $className The class name. - * @param PaletteBuilder $paletteBuilder The palette builder in use. + * @param class-string $className The class name. + * @param PaletteBuilder $paletteBuilder The palette builder in use. */ public function __construct($className, PaletteBuilder $paletteBuilder) { @@ -52,20 +57,20 @@ public function __construct($className, PaletteBuilder $paletteBuilder) /** * Set the class name. * - * @param string $className The class name. + * @param class-string $className The class name. * * @return SetPaletteConditionChainClassNameEvent */ public function setPaletteConditionChainClassName($className) { - $this->className = (string) $className; + $this->className = $className; return $this; } /** * Retrieve the class name. * - * @return string + * @return class-string */ public function getPaletteConditionChainClassName() { diff --git a/src/DataDefinition/Palette/Builder/Event/SetPalettePropertyValueConditionClassNameEvent.php b/src/DataDefinition/Palette/Builder/Event/SetPalettePropertyValueConditionClassNameEvent.php index 46b92d1a..92b0c962 100644 --- a/src/DataDefinition/Palette/Builder/Event/SetPalettePropertyValueConditionClassNameEvent.php +++ b/src/DataDefinition/Palette/Builder/Event/SetPalettePropertyValueConditionClassNameEvent.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2020 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2020 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -22,6 +23,7 @@ namespace ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Builder\Event; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Builder\PaletteBuilder; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Condition\Palette\PaletteConditionInterface; /** * This event gets emitted when a palette property value condition class name is set. @@ -35,15 +37,15 @@ class SetPalettePropertyValueConditionClassNameEvent extends BuilderEvent /** * The class name. * - * @var string + * @var class-string */ protected $className; /** * Create a new instance. * - * @param string $className The class name. - * @param PaletteBuilder $paletteBuilder The palette builder in use. + * @param class-string $className The class name. + * @param PaletteBuilder $paletteBuilder The palette builder in use. */ public function __construct($className, PaletteBuilder $paletteBuilder) { @@ -54,13 +56,13 @@ public function __construct($className, PaletteBuilder $paletteBuilder) /** * Set the class name. * - * @param string $className The class name. + * @param class-string $className The class name. * * @return SetPalettePropertyValueConditionClassNameEvent */ public function setPalettePropertyValueConditionClassName($className) { - $this->className = (string) $className; + $this->className = $className; return $this; } @@ -68,7 +70,7 @@ public function setPalettePropertyValueConditionClassName($className) /** * Retrieve the class name. * - * @return string + * @return class-string */ public function getPalettePropertyValueConditionClassName() { diff --git a/src/DataDefinition/Palette/Builder/Event/SetPropertyClassNameEvent.php b/src/DataDefinition/Palette/Builder/Event/SetPropertyClassNameEvent.php index 1cdaf7bb..5c4e83a0 100644 --- a/src/DataDefinition/Palette/Builder/Event/SetPropertyClassNameEvent.php +++ b/src/DataDefinition/Palette/Builder/Event/SetPropertyClassNameEvent.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -22,6 +23,7 @@ namespace ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Builder\Event; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Builder\PaletteBuilder; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\PropertyInterface; /** * This event gets emitted when a property class name is set. @@ -33,15 +35,15 @@ class SetPropertyClassNameEvent extends BuilderEvent /** * The class name. * - * @var string + * @var class-string */ protected $propertyClassName; /** * Create a new instance. * - * @param string $propertyClassName The class name. - * @param PaletteBuilder $paletteBuilder The palette builder in use. + * @param class-string $propertyClassName The class name. + * @param PaletteBuilder $paletteBuilder The palette builder in use. */ public function __construct($propertyClassName, PaletteBuilder $paletteBuilder) { @@ -52,13 +54,13 @@ public function __construct($propertyClassName, PaletteBuilder $paletteBuilder) /** * Set the class name. * - * @param string $propertyClassName The class name. + * @param class-string $propertyClassName The class name. * * @return SetPropertyClassNameEvent */ public function setPropertyClassName($propertyClassName) { - $this->propertyClassName = (string) $propertyClassName; + $this->propertyClassName = $propertyClassName; return $this; } @@ -66,7 +68,7 @@ public function setPropertyClassName($propertyClassName) /** * Retrieve the class name. * - * @return string + * @return class-string */ public function getPropertyClassName() { diff --git a/src/DataDefinition/Palette/Builder/Event/SetPropertyConditionChainClassNameEvent.php b/src/DataDefinition/Palette/Builder/Event/SetPropertyConditionChainClassNameEvent.php index 35e41aad..237c086e 100644 --- a/src/DataDefinition/Palette/Builder/Event/SetPropertyConditionChainClassNameEvent.php +++ b/src/DataDefinition/Palette/Builder/Event/SetPropertyConditionChainClassNameEvent.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,17 +14,23 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ namespace ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Builder\Event; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ConditionChainInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Builder\PaletteBuilder; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Condition\Property\PropertyConditionInterface; /** * This event gets emitted when a property condition chain class name is set. + * + * @psalm-type TConditionInterface=ConditionChainInterface&PropertyConditionInterface + * */ class SetPropertyConditionChainClassNameEvent extends BuilderEvent { @@ -33,15 +39,15 @@ class SetPropertyConditionChainClassNameEvent extends BuilderEvent /** * The class name. * - * @var string + * @var class-string */ protected $className; /** * Create a new instance. * - * @param string $className The class name. - * @param PaletteBuilder $paletteBuilder The palette builder in use. + * @param class-string $className The class name. + * @param PaletteBuilder $paletteBuilder The palette builder in use. */ public function __construct($className, PaletteBuilder $paletteBuilder) { @@ -52,13 +58,13 @@ public function __construct($className, PaletteBuilder $paletteBuilder) /** * Set the class name. * - * @param string $className The class name. + * @param class-string $className The class name. * * @return SetPropertyConditionChainClassNameEvent */ public function setPalettePropertyConditionChainClassName($className) { - $this->className = (string) $className; + $this->className = $className; return $this; } @@ -66,7 +72,7 @@ public function setPalettePropertyConditionChainClassName($className) /** * Retrieve the class name. * - * @return string + * @return class-string */ public function getPalettePropertyConditionChainClassName() { diff --git a/src/DataDefinition/Palette/Builder/Event/SetPropertyValueConditionClassNameEvent.php b/src/DataDefinition/Palette/Builder/Event/SetPropertyValueConditionClassNameEvent.php index b5cdb359..59a49aba 100644 --- a/src/DataDefinition/Palette/Builder/Event/SetPropertyValueConditionClassNameEvent.php +++ b/src/DataDefinition/Palette/Builder/Event/SetPropertyValueConditionClassNameEvent.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -22,6 +23,7 @@ namespace ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Builder\Event; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Builder\PaletteBuilder; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Condition\Property\PropertyConditionInterface; /** * This event gets emitted when a property value condition class name is set. @@ -33,15 +35,15 @@ class SetPropertyValueConditionClassNameEvent extends BuilderEvent /** * The class name. * - * @var string + * @var class-string */ protected $className; /** * Create a new instance. * - * @param string $className The class name. - * @param PaletteBuilder $paletteBuilder The palette builder in use. + * @param class-string $className The class name. + * @param PaletteBuilder $paletteBuilder The palette builder in use. */ public function __construct($className, PaletteBuilder $paletteBuilder) { @@ -52,20 +54,20 @@ public function __construct($className, PaletteBuilder $paletteBuilder) /** * Set the class name. * - * @param string $className The class name. + * @param class-string $className The class name. * * @return SetPropertyValueConditionClassNameEvent */ public function setPropertyValueConditionClassName($className) { - $this->className = (string) $className; + $this->className = $className; return $this; } /** * Retrieve the class name. * - * @return string + * @return class-string */ public function getPropertyValueConditionClassName() { diff --git a/src/DataDefinition/Palette/Builder/PaletteBuilder.php b/src/DataDefinition/Palette/Builder/PaletteBuilder.php index e80c921f..df1fd138 100644 --- a/src/DataDefinition/Palette/Builder/PaletteBuilder.php +++ b/src/DataDefinition/Palette/Builder/PaletteBuilder.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2021 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -15,7 +15,8 @@ * @author Tristan Lins * @author Sven Baumann * @author Richard Henkenjohann - * @copyright 2013-2021 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -75,10 +76,20 @@ use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\PropertyInterface; use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralInvalidArgumentException; use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralRuntimeException; +use ReflectionClass; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; + +use function array_shift; +use function count; +use function func_get_args; +use function is_object; /** * The palette builder is used to build palette collections, palettes, legends, properties and conditions. * + * @psalm-type TPaletteConditionChainInterface=ConditionChainInterface&PaletteConditionInterface + * @psalm-type TPropertyConditionChainInterface=ConditionChainInterface&PropertyConditionInterface + * * @SuppressWarnings(PHPMD.LongVariable) * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) * @SuppressWarnings(PHPMD.TooManyPublicMethods) @@ -108,126 +119,126 @@ class PaletteBuilder /** * The class name of the class to use for palette collections. * - * @var string + * @var class-string */ protected $paletteCollectionClassName = PaletteCollection::class; /** * The class to use for palette collections. * - * @var \ReflectionClass + * @var ReflectionClass */ protected $paletteCollectionClass; /** * The class name of the class to use for palettes. * - * @var string + * @var class-string */ protected $paletteClassName = Palette::class; /** * The class to use for palettes. * - * @var \ReflectionClass + * @var ReflectionClass */ protected $paletteClass; /** * The class name of the class to use for palette legends. * - * @var string + * @var class-string */ protected $legendClassName = Legend::class; /** * The class to use for palette legends. * - * @var \ReflectionClass + * @var ReflectionClass */ protected $legendClass; /** * The class name of the class to use for palette properties. * - * @var string + * @var class-string */ protected $propertyClassName = Property::class; /** * The class to use for palette properties. * - * @var \ReflectionClass + * @var ReflectionClass */ protected $propertyClass; /** * The class name of the class to use for palette condition chains. * - * @var string + * @var class-string */ protected $paletteConditionChainClassName = PaletteConditionChain::class; /** - * The the class to use for palette condition chains. + * The class to use for palette condition chains. * - * @var \ReflectionClass + * @var ReflectionClass */ protected $paletteConditionChainClass; /** * The class name of the class to use for palette conditions. * - * @var string + * @var class-string */ protected $defaultPaletteConditionClassName = DefaultPaletteCondition::class; /** - * The the class to use for palette conditions. + * The class to use for palette conditions. * - * @var \ReflectionClass + * @var ReflectionClass */ protected $defaultPaletteConditionClass; /** * The class name of the class to use for property value conditions. * - * @var string + * @var class-string */ protected $palettePropertyValueConditionClassName = PropertyValueCondition::class; /** * The class to use for property value conditions. * - * @var \ReflectionClass + * @var ReflectionClass */ protected $palettePropertyValueConditionClass; /** * The class name of the class to use for property condition chains. * - * @var string + * @var class-string */ protected $propertyConditionChainClassName = PropertyConditionChain::class; /** * The class to use for property condition chains. * - * @var \ReflectionClass + * @var ReflectionClass */ protected $propertyConditionChainClass; /** * The class name of the class to use for property value conditions. * - * @var string + * @var class-string */ protected $propertyValueConditionClassName = PropertyPropertyValueCondition::class; /** - * The the class to use for property value conditions. + * The class to use for property value conditions. * - * @var \ReflectionClass + * @var ReflectionClass */ protected $propertyValueConditionClass; @@ -255,14 +266,14 @@ class PaletteBuilder /** * The property currently working on. * - * @var PropertyInterface|null + * @var list|PropertyInterface|null */ protected $property; /** * The condition currently working on. * - * @var PropertyConditionInterface|PaletteConditionInterface|ConditionChainInterface|null + * @var PropertyConditionInterface|PaletteConditionInterface|null */ protected $condition; @@ -271,7 +282,7 @@ class PaletteBuilder * * @param ContainerInterface $container The data definition container for which the palettes shall get built. * - * @return PaletteBuilder + * @return self */ public static function create(ContainerInterface $container) { @@ -287,17 +298,17 @@ public function __construct(ContainerInterface $container) { $this->container = $container; - $this->paletteCollectionClass = new \ReflectionClass($this->paletteCollectionClassName); - $this->paletteClass = new \ReflectionClass($this->paletteClassName); - $this->legendClass = new \ReflectionClass($this->legendClassName); - $this->propertyClass = new \ReflectionClass($this->propertyClassName); + $this->paletteCollectionClass = new ReflectionClass($this->paletteCollectionClassName); + $this->paletteClass = new ReflectionClass($this->paletteClassName); + $this->legendClass = new ReflectionClass($this->legendClassName); + $this->propertyClass = new ReflectionClass($this->propertyClassName); - $this->paletteConditionChainClass = new \ReflectionClass($this->paletteConditionChainClassName); - $this->defaultPaletteConditionClass = new \ReflectionClass($this->defaultPaletteConditionClassName); - $this->palettePropertyValueConditionClass = new \ReflectionClass($this->palettePropertyValueConditionClassName); + $this->paletteConditionChainClass = new ReflectionClass($this->paletteConditionChainClassName); + $this->defaultPaletteConditionClass = new ReflectionClass($this->defaultPaletteConditionClassName); + $this->palettePropertyValueConditionClass = new ReflectionClass($this->palettePropertyValueConditionClassName); - $this->propertyConditionChainClass = new \ReflectionClass($this->propertyConditionChainClassName); - $this->propertyValueConditionClass = new \ReflectionClass($this->propertyValueConditionClassName); + $this->propertyConditionChainClass = new ReflectionClass($this->propertyConditionChainClassName); + $this->propertyValueConditionClass = new ReflectionClass($this->propertyValueConditionClassName); } /** @@ -313,9 +324,9 @@ public function getContainer() /** * Set the palette collection class name. * - * @param string $paletteCollectionClassName The class name. + * @param class-string $paletteCollectionClassName The class name. * - * @return PaletteBuilder + * @return self */ public function setPaletteCollectionClassName($paletteCollectionClassName) { @@ -323,15 +334,16 @@ public function setPaletteCollectionClassName($paletteCollectionClassName) $this->dispatchEvent($event); $paletteCollectionClassName = $event->getPaletteCollectionClassName(); - $this->paletteCollectionClassName = (string) $paletteCollectionClassName; - $this->paletteCollectionClass = new \ReflectionClass($this->paletteCollectionClassName); + $this->paletteCollectionClassName = $paletteCollectionClassName; + $this->paletteCollectionClass = new ReflectionClass($this->paletteCollectionClassName); + return $this; } /** * Return the palette collection class name. * - * @return string + * @return class-string */ public function getPaletteCollectionClassName() { @@ -341,9 +353,9 @@ public function getPaletteCollectionClassName() /** * Set the palette class name. * - * @param string $paletteClassName The class name. + * @param class-string $paletteClassName The class name. * - * @return PaletteBuilder + * @return self */ public function setPaletteClassName($paletteClassName) { @@ -351,15 +363,16 @@ public function setPaletteClassName($paletteClassName) $this->dispatchEvent($event); $paletteClassName = $event->getPaletteClassName(); - $this->paletteClassName = (string) $paletteClassName; - $this->paletteClass = new \ReflectionClass($this->paletteClassName); + $this->paletteClassName = $paletteClassName; + $this->paletteClass = new ReflectionClass($this->paletteClassName); + return $this; } /** * Return the palette class name. * - * @return string + * @return class-string */ public function getPaletteClassName() { @@ -369,9 +382,9 @@ public function getPaletteClassName() /** * Set the legend class name. * - * @param string $legendClassName The class name. + * @param class-string $legendClassName The class name. * - * @return PaletteBuilder + * @return self */ public function setLegendClassName($legendClassName) { @@ -379,15 +392,16 @@ public function setLegendClassName($legendClassName) $this->dispatchEvent($event); $legendClassName = $event->getLegendClassName(); - $this->legendClassName = (string) $legendClassName; - $this->legendClass = new \ReflectionClass($this->legendClassName); + $this->legendClassName = $legendClassName; + $this->legendClass = new ReflectionClass($this->legendClassName); + return $this; } /** * Return the legend class name. * - * @return string + * @return class-string */ public function getLegendClassName() { @@ -397,9 +411,9 @@ public function getLegendClassName() /** * Set the property class name. * - * @param string $propertyClassName The class name. + * @param class-string $propertyClassName The class name. * - * @return PaletteBuilder + * @return self */ public function setPropertyClassName($propertyClassName) { @@ -407,15 +421,16 @@ public function setPropertyClassName($propertyClassName) $this->dispatchEvent($event); $propertyClassName = $event->getPropertyClassName(); - $this->propertyClassName = (string) $propertyClassName; - $this->propertyClass = new \ReflectionClass($this->propertyClassName); + $this->propertyClassName = $propertyClassName; + $this->propertyClass = new ReflectionClass($this->propertyClassName); + return $this; } /** * Return the property class name. * - * @return string + * @return class-string */ public function getPropertyClassName() { @@ -425,9 +440,9 @@ public function getPropertyClassName() /** * Set the palette condition chain class name. * - * @param string $paletteConditionChainClassName The class name. + * @param class-string $paletteConditionChainClassName The class name. * - * @return PaletteBuilder + * @return self */ public function setPaletteConditionChainClassName($paletteConditionChainClassName) { @@ -435,15 +450,16 @@ public function setPaletteConditionChainClassName($paletteConditionChainClassNam $this->dispatchEvent($event); $paletteConditionChainClassName = $event->getPaletteConditionChainClassName(); - $this->paletteConditionChainClassName = (string) $paletteConditionChainClassName; - $this->paletteConditionChainClass = new \ReflectionClass($this->paletteConditionChainClassName); + $this->paletteConditionChainClassName = $paletteConditionChainClassName; + $this->paletteConditionChainClass = new ReflectionClass($this->paletteConditionChainClassName); + return $this; } /** * Return the palette condition chain class name. * - * @return string + * @return class-string */ public function getPaletteConditionChainClassName() { @@ -463,9 +479,9 @@ public function getPaletteCollection() /** * Set the default palette condition class name. * - * @param string $defaultPaletteConditionClassName The class name. + * @param class-string $defaultPaletteConditionClassName The class name. * - * @return PaletteBuilder + * @return self */ public function setDefaultPaletteConditionClassName($defaultPaletteConditionClassName) { @@ -473,15 +489,16 @@ public function setDefaultPaletteConditionClassName($defaultPaletteConditionClas $this->dispatchEvent($event); $defaultPaletteConditionClassName = $event->getDefaultPaletteConditionClassName(); - $this->defaultPaletteConditionClassName = (string) $defaultPaletteConditionClassName; - $this->defaultPaletteConditionClass = new \ReflectionClass($this->defaultPaletteConditionClassName); + $this->defaultPaletteConditionClassName = $defaultPaletteConditionClassName; + $this->defaultPaletteConditionClass = new ReflectionClass($this->defaultPaletteConditionClassName); + return $this; } /** * Return the default palette condition class name. * - * @return string + * @return class-string */ public function getDefaultPaletteConditionClassName() { @@ -491,9 +508,9 @@ public function getDefaultPaletteConditionClassName() /** * Set the palette property value condition class name. * - * @param string $palettePropertyValueConditionClassName The class name. + * @param class-string $palettePropertyValueConditionClassName The class name. * - * @return PaletteBuilder + * @return self */ public function setPalettePropertyValueConditionClassName($palettePropertyValueConditionClassName) { @@ -501,17 +518,18 @@ public function setPalettePropertyValueConditionClassName($palettePropertyValueC $this->dispatchEvent($event); $palettePropertyValueConditionClassName = $event->getPalettePropertyValueConditionClassName(); - $this->palettePropertyValueConditionClassName = (string) $palettePropertyValueConditionClassName; - $this->palettePropertyValueConditionClass = new \ReflectionClass( + $this->palettePropertyValueConditionClassName = $palettePropertyValueConditionClassName; + $this->palettePropertyValueConditionClass = new ReflectionClass( $this->palettePropertyValueConditionClassName ); + return $this; } /** * Return the palette property value condition class name. * - * @return string + * @return class-string */ public function getPalettePropertyValueConditionClassName() { @@ -521,9 +539,9 @@ public function getPalettePropertyValueConditionClassName() /** * Set the property condition chain class name. * - * @param string $propertyConditionChainClassName The class name. + * @param class-string $propertyConditionChainClassName The class name. * - * @return PaletteBuilder + * @return self */ public function setPropertyConditionChainClassName($propertyConditionChainClassName) { @@ -531,15 +549,16 @@ public function setPropertyConditionChainClassName($propertyConditionChainClassN $this->dispatchEvent($event); $propertyConditionChainClassName = $event->getPalettePropertyConditionChainClassName(); - $this->propertyConditionChainClassName = (string) $propertyConditionChainClassName; - $this->propertyConditionChainClass = new \ReflectionClass($this->propertyConditionChainClassName); + $this->propertyConditionChainClassName = $propertyConditionChainClassName; + $this->propertyConditionChainClass = new ReflectionClass($this->propertyConditionChainClassName); + return $this; } /** * Return the property condition chain class name. * - * @return string + * @return class-string */ public function getPropertyConditionChainClassName() { @@ -549,9 +568,9 @@ public function getPropertyConditionChainClassName() /** * Set the property value condition class name. * - * @param string $propertyValueConditionClassName The class name. + * @param class-string $propertyValueConditionClassName The class name. * - * @return PaletteBuilder + * @return self */ public function setPropertyValueConditionClassName($propertyValueConditionClassName) { @@ -559,15 +578,16 @@ public function setPropertyValueConditionClassName($propertyValueConditionClassN $this->dispatchEvent($event); $propertyValueConditionClassName = $event->getPropertyValueConditionClassName(); - $this->propertyValueConditionClassName = (string) $propertyValueConditionClassName; - $this->propertyValueConditionClass = new \ReflectionClass($this->propertyValueConditionClassName); + $this->propertyValueConditionClassName = $propertyValueConditionClassName; + $this->propertyValueConditionClass = new ReflectionClass($this->propertyValueConditionClassName); + return $this; } /** * Return the property value condition class name. * - * @return string + * @return class-string */ public function getPropertyValueConditionClassName() { @@ -597,7 +617,7 @@ public function getLegend() /** * Return the current property object. * - * @return PropertyInterface|null + * @return list|PropertyInterface|null */ public function getProperty() { @@ -607,7 +627,7 @@ public function getProperty() /** * Return the current condition object. * - * @return PaletteConditionInterface|PropertyConditionInterface|null + * @return PropertyConditionInterface|PaletteConditionInterface|null */ public function getCondition() { @@ -619,7 +639,7 @@ public function getCondition() * * @param PaletteCollectionInterface $paletteCollection The palette collection to reuse. * - * @return PaletteBuilder + * @return self */ public function usePaletteCollection(PaletteCollectionInterface $paletteCollection) { @@ -637,7 +657,7 @@ public function usePaletteCollection(PaletteCollectionInterface $paletteCollecti /** * Start a new palette collection. * - * @return PaletteBuilder + * @return self */ public function createPaletteCollection() { @@ -659,7 +679,7 @@ public function createPaletteCollection() * * @param PaletteCollectionInterface $collection Return the final palette collection. * - * @return PaletteBuilder + * @return self * * @throws DcGeneralRuntimeException When no collection is stored. */ @@ -689,7 +709,7 @@ public function finishPaletteCollection(&$collection = null) * * @param PaletteInterface $palette The palette to reuse. * - * @return PaletteBuilder + * @return self */ public function usePalette(PaletteInterface $palette) { @@ -710,7 +730,7 @@ public function usePalette(PaletteInterface $palette) * @param string|null $name Only for backwards compatibility, We will remove palette names in the future * (deprecated). * - * @return PaletteBuilder + * @return self */ public function createPalette($name = null) { @@ -736,7 +756,7 @@ public function createPalette($name = null) * * @param PaletteInterface $palette Return the final palette. * - * @return PaletteBuilder + * @return self * * @throws DcGeneralRuntimeException When no palette is stored in the builder. */ @@ -771,7 +791,7 @@ public function finishPalette(&$palette = null) * * @param LegendInterface $legend The legend. * - * @return PaletteBuilder + * @return self */ public function useLegend(LegendInterface $legend) { @@ -791,7 +811,7 @@ public function useLegend(LegendInterface $legend) * * @param string $name Name of the legend. * - * @return PaletteBuilder + * @return self */ public function createLegend($name) { @@ -813,7 +833,7 @@ public function createLegend($name) * * @param LegendInterface $legend Return the final legend. * - * @return PaletteBuilder + * @return self * * @throws DcGeneralRuntimeException When no legend is stored in the builder. */ @@ -846,7 +866,7 @@ public function finishLegend(&$legend = null) * @param PropertyInterface $property The first property. * @param PropertyInterface $_ Any more subsequent properties to be used. * - * @return PaletteBuilder + * @return self * * @SuppressWarnings(PHPMD.UnusedFormalParameter) * @SuppressWarnings(PHPMD.ShortVariable) @@ -858,7 +878,8 @@ public function useProperty($property, $_ = null) $this->finishProperty(); } - $properties = \func_get_args(); + /** @var list $properties */ + $properties = func_get_args(); $this->property = []; foreach ($properties as $property) { @@ -868,8 +889,8 @@ public function useProperty($property, $_ = null) $this->property[] = $property; } - if (1 === \count($this->property)) { - $this->property = \array_shift($this->property); + if (1 === count($this->property)) { + $this->property = array_shift($this->property); } return $this; @@ -881,7 +902,7 @@ public function useProperty($property, $_ = null) * @param string $propertyName The name of the property. * @param PropertyInterface $_ Any more subsequent property names to be used. * - * @return PaletteBuilder + * @return self * * @SuppressWarnings(PHPMD.UnusedFormalParameter) * @SuppressWarnings(PHPMD.ShortVariable) @@ -893,7 +914,7 @@ public function createProperty($propertyName, $_ = null) $this->finishProperty(); } - $propertyNames = \func_get_args(); + $propertyNames = func_get_args(); $this->property = []; foreach ($propertyNames as $propertyName) { @@ -906,8 +927,8 @@ public function createProperty($propertyName, $_ = null) $this->property[] = $property; } - if (1 === \count($this->property)) { - $this->property = \array_shift($this->property); + if (1 === count($this->property)) { + $this->property = array_shift($this->property); } return $this; @@ -918,7 +939,7 @@ public function createProperty($propertyName, $_ = null) * * @param PropertyInterface|PropertyInterface[] $property Return the final property or set of properties. * - * @return PaletteBuilder + * @return self * * @throws DcGeneralRuntimeException When no property is stored in the builder. */ @@ -932,7 +953,7 @@ public function finishProperty(&$property = null) $this->finishCondition(); } - $properties = \is_object($this->property) ? [$this->property] : $this->property; + $properties = is_object($this->property) ? [$this->property] : $this->property; foreach ($properties as $index => $tempProperty) { $event = new FinishPropertyEvent($tempProperty, $this); @@ -954,11 +975,13 @@ public function finishProperty(&$property = null) /** * Create a palette condition chain. * - * @return PaletteBuilder + * @return self + * + * @psalm-assert ConditionChainInterface $this->condition */ protected function createPaletteConditionChain() { - if (!$this->condition instanceof PaletteConditionChain) { + if (!$this->condition instanceof ConditionChainInterface) { $previousCondition = $this->condition; $condition = $this->paletteConditionChainClass->newInstance(); @@ -969,8 +992,12 @@ protected function createPaletteConditionChain() $event = new CreateConditionEvent($condition, $this); $this->dispatchEvent($event); $condition = $event->getCondition(); - /** @var ConditionChainInterface $condition */ - $condition->addCondition($previousCondition); + assert($condition instanceof PaletteConditionInterface); + assert($condition instanceof ConditionChainInterface); + + if ($previousCondition) { + $condition->addCondition($previousCondition); + } $this->condition = $condition; } @@ -983,12 +1010,15 @@ protected function createPaletteConditionChain() * * @param string $conjunction The conjunction to use (defaults to AND). * - * @return PaletteBuilder + * @return self + * + * @psalm-assert ConditionChainInterface $this->condition */ - protected function createPropertyConditionChain($conjunction = PropertyConditionChain::AND_CONJUNCTION) + protected function createPropertyConditionChain($conjunction = ConditionChainInterface::AND_CONJUNCTION) { if ( - !($this->condition instanceof PropertyConditionChain) + // FIXME: We need a condition chain interface here to allow proper overriding. + !($this->condition instanceof ConditionChainInterface) || ($conjunction !== $this->condition->getConjunction()) ) { $previousCondition = $this->condition; @@ -1001,8 +1031,9 @@ protected function createPropertyConditionChain($conjunction = PropertyCondition $event = new CreateConditionEvent($condition, $this); $this->dispatchEvent($event); $condition = $event->getCondition(); - /** @var ConditionChainInterface $condition */ - $condition->addCondition($previousCondition); + if (null !== $previousCondition) { + $condition->addCondition($previousCondition); + } $this->condition = $condition; } @@ -1013,7 +1044,7 @@ protected function createPropertyConditionChain($conjunction = PropertyCondition /** * Start a new default-palette condition. * - * @return PaletteBuilder + * @return self * * @throws DcGeneralRuntimeException When no palette or property has been stored. */ @@ -1044,7 +1075,7 @@ public function createDefaultPaletteCondition() /** * Start a new default-palette condition and chain with previous condition. * - * @return PaletteBuilder + * @return self * * @throws DcGeneralRuntimeException When no palette or property has been stored. */ @@ -1079,7 +1110,7 @@ public function chainDefaultPaletteCondition() * @param mixed $propertyValue The value of the property. * @param bool $strict Flag if the comparison shall be strict (type safe). * - * @return PaletteBuilder + * @return self * * @throws DcGeneralRuntimeException If neither a palette nor a property is stored in the builder. */ @@ -1098,15 +1129,17 @@ public function createPropertyValueCondition($propertyName, $propertyValue, $str 'Does not know where to create the property-value condition, please create a palette or property first' ); } - + // FIXME: we need an interface for PropertyValueCondition + assert( + ($condition instanceof PropertyValueCondition) + || ($condition instanceof PropertyPropertyValueCondition) + ); $condition->setPropertyName($propertyName); $condition->setPropertyValue($propertyValue); $condition->setStrict($strict); - $event = new CreatePropertyValueConditionEvent($condition, $this); $this->dispatchEvent($event); $condition = $event->getPropertyValueCondition(); - $event = new CreateConditionEvent($condition, $this); $this->dispatchEvent($event); $condition = $event->getCondition(); @@ -1124,7 +1157,7 @@ public function createPropertyValueCondition($propertyName, $propertyValue, $str * @param bool $strict Flag if the comparison shall be strict (type safe). * @param string $conjunction The conjunction. * - * @return PaletteBuilder + * @return self * * @throws DcGeneralRuntimeException If neither a palette nor a property is stored in the builder. */ @@ -1146,14 +1179,17 @@ public function chainPropertyValueCondition( ); } + // FIXME: we need an interface for PropertyValueCondition + assert( + ($condition instanceof PropertyValueCondition) + || ($condition instanceof PropertyPropertyValueCondition) + ); $condition->setPropertyName($propertyName); $condition->setPropertyValue($propertyValue); $condition->setStrict($strict); - $event = new CreatePropertyValueConditionEvent($condition, $this); $this->dispatchEvent($event); $condition = $event->getPropertyValueCondition(); - $event = new CreateConditionEvent($condition, $this); $this->dispatchEvent($event); $condition = $event->getCondition(); @@ -1168,7 +1204,7 @@ public function chainPropertyValueCondition( * * @param PropertyConditionInterface|PaletteConditionInterface $condition Return the final condition. * - * @return PaletteBuilder + * @return self * * @throws DcGeneralRuntimeException If no condition is stored in the builder. */ @@ -1230,17 +1266,19 @@ protected function addPaletteCondition(PaletteConditionInterface $condition) * @return void * * @throws DcGeneralRuntimeException If the the property is missing. + * + * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ protected function addPropertyCondition(PropertyConditionInterface $condition, $scope = self::VISIBLE) { - if (!$this->property) { + if (null === $this->property || [] === $this->property) { throw new DcGeneralRuntimeException('Property is missing, please create a property first'); } - $properties = \is_object($this->property) ? [$this->property] : $this->property; + $properties = ($this->property instanceof PropertyInterface) ? [$this->property] : $this->property; foreach ($properties as $property) { - /** @var PropertyInterface $property */ + /** @var AddConditionEvent $event */ $event = new AddConditionEvent($condition, $property, $this); $this->dispatchEvent($event); $condition = $event->getCondition(); @@ -1277,7 +1315,7 @@ protected function addPropertyCondition(PropertyConditionInterface $condition, $ * @param PaletteConditionInterface|PropertyConditionInterface $condition The condition to add. * @param string $scope The scope. * - * @return PaletteBuilder + * @return self * * @throws DcGeneralInvalidArgumentException When an unknown condition type is passed. */ @@ -1293,8 +1331,9 @@ public function addCondition($condition, $scope = self::VISIBLE) return $this; } - $type = \is_object($condition) ? \get_class($condition) : \gettype($condition); - throw new DcGeneralInvalidArgumentException('Cannot handle condition of type [' . $type . ']'); + throw new DcGeneralInvalidArgumentException( + 'Cannot handle condition of type [' . \get_debug_type($condition) . ']' + ); } /** @@ -1309,6 +1348,8 @@ public function addCondition($condition, $scope = self::VISIBLE) protected function dispatchEvent(BuilderEvent $event): void { $dispatcher = System::getContainer()->get('event_dispatcher'); + assert($dispatcher instanceof EventDispatcherInterface); + $dispatcher->dispatch($event, $event::NAME); } } diff --git a/src/DataDefinition/Palette/Condition/Palette/AbstractBoolPaletteCondition.php b/src/DataDefinition/Palette/Condition/Palette/AbstractBoolPaletteCondition.php index 989f93cd..9726282b 100644 --- a/src/DataDefinition/Palette/Condition/Palette/AbstractBoolPaletteCondition.php +++ b/src/DataDefinition/Palette/Condition/Palette/AbstractBoolPaletteCondition.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -49,8 +50,8 @@ abstract class AbstractBoolPaletteCondition extends AbstractWeightAwarePaletteCo */ public function __construct($propertyName = '', $strict = false, $weight = 1) { - $this->propertyName = (string) $propertyName; - $this->strict = (bool) $strict; + $this->propertyName = $propertyName; + $this->strict = $strict; $this->setWeight($weight); } @@ -63,7 +64,7 @@ public function __construct($propertyName = '', $strict = false, $weight = 1) */ public function setPropertyName($propertyName) { - $this->propertyName = (string) $propertyName; + $this->propertyName = $propertyName; return $this; } @@ -87,7 +88,7 @@ public function getPropertyName() */ public function setStrict($strict) { - $this->strict = (bool) $strict; + $this->strict = $strict; return $this; } diff --git a/src/DataDefinition/Palette/Condition/Palette/PaletteConditionChain.php b/src/DataDefinition/Palette/Condition/Palette/PaletteConditionChain.php index d453088b..51e4866a 100644 --- a/src/DataDefinition/Palette/Condition/Palette/PaletteConditionChain.php +++ b/src/DataDefinition/Palette/Condition/Palette/PaletteConditionChain.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -34,7 +35,7 @@ class PaletteConditionChain extends AbstractConditionChain implements PaletteCon /** * {@inheritdoc} * - * @throws DcGeneralRuntimeException When an condition that does not implement PaletteConditionInterface + * @throws DcGeneralRuntimeException When a condition that does not implement PaletteConditionInterface * is encountered. */ public function getMatchCount(ModelInterface $model = null, PropertyValueBag $input = null) @@ -49,6 +50,9 @@ public function getMatchCount(ModelInterface $model = null, PropertyValueBag $in $conditionCount = $condition->getMatchCount($model, $input); if (false !== $conditionCount) { + if (false === $totalCount) { + $totalCount = 0; + } $totalCount += $conditionCount; } elseif (static::AND_CONJUNCTION === $this->conjunction) { return false; diff --git a/src/DataDefinition/Palette/Condition/Palette/PaletteConditionInterface.php b/src/DataDefinition/Palette/Condition/Palette/PaletteConditionInterface.php index 2af7520f..1e57e5e3 100644 --- a/src/DataDefinition/Palette/Condition/Palette/PaletteConditionInterface.php +++ b/src/DataDefinition/Palette/Condition/Palette/PaletteConditionInterface.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -40,10 +41,10 @@ interface PaletteConditionInterface extends ConditionInterface * When the condition does match, it must return a numeric value, the value may be negative or positive and even * zero. * - * @param ModelInterface|null $model If given, selectors will be evaluated depending on the model. - * @param PropertyValueBag $input If given, selectors will be evaluated depending on the input data. + * @param ModelInterface|null $model If given, selectors will be evaluated depending on the model. + * @param PropertyValueBag|null $input If given, selectors will be evaluated depending on the input data. * - * @return bool|int + * @return false|int */ public function getMatchCount(ModelInterface $model = null, PropertyValueBag $input = null); diff --git a/src/DataDefinition/Palette/Condition/Palette/PropertyValueCondition.php b/src/DataDefinition/Palette/Condition/Palette/PropertyValueCondition.php index c29f4320..c6c2843a 100644 --- a/src/DataDefinition/Palette/Condition/Palette/PropertyValueCondition.php +++ b/src/DataDefinition/Palette/Condition/Palette/PropertyValueCondition.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -60,9 +61,9 @@ class PropertyValueCondition extends AbstractWeightAwarePaletteCondition */ public function __construct($propertyName = '', $propertyValue = null, $strict = false, $weight = 1) { - $this->propertyName = (string) $propertyName; + $this->propertyName = $propertyName; $this->propertyValue = $propertyValue; - $this->strict = (bool) $strict; + $this->strict = $strict; $this->setWeight($weight); } @@ -75,7 +76,7 @@ public function __construct($propertyName = '', $propertyValue = null, $strict = */ public function setPropertyName($propertyName) { - $this->propertyName = (string) $propertyName; + $this->propertyName = $propertyName; return $this; } @@ -121,7 +122,7 @@ public function getPropertyValue() */ public function setStrict($strict) { - $this->strict = (bool) $strict; + $this->strict = $strict; return $this; } diff --git a/src/DataDefinition/Palette/Condition/Property/BooleanCondition.php b/src/DataDefinition/Palette/Condition/Property/BooleanCondition.php index b322c32b..889dd176 100644 --- a/src/DataDefinition/Palette/Condition/Property/BooleanCondition.php +++ b/src/DataDefinition/Palette/Condition/Property/BooleanCondition.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -45,7 +46,7 @@ class BooleanCondition implements PropertyConditionInterface */ public function __construct($value) { - $this->value = (bool) $value; + $this->value = $value; } /** @@ -57,7 +58,7 @@ public function __construct($value) */ public function setValue($value) { - $this->value = (bool) $value; + $this->value = $value; return $this; } diff --git a/src/DataDefinition/Palette/Condition/Property/DumpingPropertyCondition.php b/src/DataDefinition/Palette/Condition/Property/DumpingPropertyCondition.php index 14798ea7..8a04ce57 100644 --- a/src/DataDefinition/Palette/Condition/Property/DumpingPropertyCondition.php +++ b/src/DataDefinition/Palette/Condition/Property/DumpingPropertyCondition.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2022 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -15,7 +15,8 @@ * @author Tristan Lins * @author Sven Baumann * @author David Molineus - * @copyright 2013-2022 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -54,6 +55,8 @@ public function __construct($propertyCondition) * {@inheritdoc} * * @SuppressWarnings(PHPMD.DevelopmentCodeFragment) + * + * @psalm-suppress ForbiddenCode - We explicitly allow var_dump() here for debugging purposes. */ public function match( ModelInterface $model = null, @@ -63,7 +66,7 @@ public function match( ) { $result = $this->propertyCondition->match($model, $input, $property, $legend); - // @codingStandardsIgnoreStart - We explicitely allow var_dump() here for debugging purposes. + // @codingStandardsIgnoreStart - We explicitly allow var_dump() here for debugging purposes. echo '
$condition: 
'; \var_dump($this->propertyCondition); echo '
$model: 
'; diff --git a/src/DataDefinition/Palette/Condition/Property/PropertyBooleanConditionTrait.php b/src/DataDefinition/Palette/Condition/Property/PropertyBooleanConditionTrait.php new file mode 100644 index 00000000..f9830418 --- /dev/null +++ b/src/DataDefinition/Palette/Condition/Property/PropertyBooleanConditionTrait.php @@ -0,0 +1,111 @@ + + * @copyright 2013-2023 Contao Community Alliance. + * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later + * @filesource + */ + +namespace ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Condition\Property; + +/** + * Condition checking that the value of a property is false. + * + * @require-implements PropertyConditionInterface + */ +trait PropertyBooleanConditionTrait +{ + /** + * The property name. + * + * @var string + */ + protected $propertyName; + + /** + * Use strict compare mode. + * + * @var bool + */ + protected $strict; + + /** + * Create a new instance. + * + * @param string $propertyName The name of the property. + * @param bool $strict Flag if the comparison shall be strict (type safe). + */ + public function __construct($propertyName, $strict = false) + { + $this->propertyName = $propertyName; + $this->strict = $strict; + } + + /** + * Set the property name. + * + * @param string $propertyName The property name. + * + * @return self + */ + public function setPropertyName($propertyName) + { + $this->propertyName = $propertyName; + + return $this; + } + + /** + * Retrieve the property name. + * + * @return string + */ + public function getPropertyName() + { + return $this->propertyName; + } + + /** + * Set the flag if the comparison shall be strict (type safe). + * + * @param boolean $strict The flag. + * + * @return self + */ + public function setStrict($strict) + { + $this->strict = $strict; + + return $this; + } + + /** + * Retrieve the flag if the comparison shall be strict (type safe). + * + * @return boolean + * + * @SuppressWarnings(PHPMD.BooleanGetMethodName) + */ + public function getStrict() + { + return $this->strict; + } + + /** + * {@inheritdoc} + */ + public function __clone() + { + } +} diff --git a/src/DataDefinition/Palette/Condition/Property/PropertyConditionInterface.php b/src/DataDefinition/Palette/Condition/Property/PropertyConditionInterface.php index 204e712f..e9ff3aad 100644 --- a/src/DataDefinition/Palette/Condition/Property/PropertyConditionInterface.php +++ b/src/DataDefinition/Palette/Condition/Property/PropertyConditionInterface.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -35,14 +36,14 @@ interface PropertyConditionInterface extends ConditionInterface /** * Check if the condition match. * - * @param ModelInterface|null $model If given, subpalettes will be evaluated depending on the model. - * If no model is given, all properties will be returned, including subpalette - * properties. - * @param PropertyValueBag $input If given, subpalettes will be evaluated depending on the input data. - * If no model and no input data is given, all properties will be returned, - * including subpalette properties. - * @param PropertyInterface $property The defined property. - * @param LegendInterface $legend The legend the property is assigned to. + * @param ModelInterface|null $model If given, subpalettes will be evaluated depending on the model. + * If no model is given, all properties will be returned, including + * subpalette properties. + * @param PropertyValueBag|null $input If given, subpalettes will be evaluated depending on the input data. + * If no model and no input data is given, all properties will be returned, + * including subpalette properties. + * @param PropertyInterface|null $property The defined property. + * @param LegendInterface|null $legend The legend the property is assigned to. * * @return bool */ diff --git a/src/DataDefinition/Palette/Condition/Property/PropertyEditableCondition.php b/src/DataDefinition/Palette/Condition/Property/PropertyEditableCondition.php index e8b030c3..ecda816c 100644 --- a/src/DataDefinition/Palette/Condition/Property/PropertyEditableCondition.php +++ b/src/DataDefinition/Palette/Condition/Property/PropertyEditableCondition.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -45,7 +46,7 @@ class PropertyEditableCondition implements PropertyConditionInterface */ public function __construct($propertyName = '') { - $this->propertyName = (string) $propertyName; + $this->propertyName = $propertyName; } /** @@ -57,7 +58,7 @@ public function __construct($propertyName = '') */ public function setPropertyName($propertyName) { - $this->propertyName = (string) $propertyName; + $this->propertyName = $propertyName; return $this; } @@ -84,8 +85,8 @@ public function match( return false; } - if ($legend->getPalette()) { - return $legend->getPalette()->getProperty($this->propertyName)->isEditable($model, $input, $legend); + if (null !== ($palette = $legend->getPalette())) { + return $palette->getProperty($this->propertyName)->isEditable($model, $input, $legend); } return $legend->getProperty($this->propertyName)->isEditable($model, $input, $legend); diff --git a/src/DataDefinition/Palette/Condition/Property/PropertyFalseCondition.php b/src/DataDefinition/Palette/Condition/Property/PropertyFalseCondition.php index 7dfb9361..9bee678b 100644 --- a/src/DataDefinition/Palette/Condition/Property/PropertyFalseCondition.php +++ b/src/DataDefinition/Palette/Condition/Property/PropertyFalseCondition.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -31,81 +32,7 @@ */ class PropertyFalseCondition implements PropertyConditionInterface { - /** - * The property name. - * - * @var string - */ - protected $propertyName; - - /** - * Use strict compare mode. - * - * @var bool - */ - protected $strict; - - /** - * Create a new instance. - * - * @param string $propertyName The name of the property. - * @param bool $strict Flag if the comparison shall be strict (type safe). - */ - public function __construct($propertyName, $strict = false) - { - $this->propertyName = (string) $propertyName; - $this->strict = (bool) $strict; - } - - /** - * Set the property name. - * - * @param string $propertyName The property name. - * - * @return PropertyFalseCondition - */ - public function setPropertyName($propertyName) - { - $this->propertyName = (string) $propertyName; - - return $this; - } - - /** - * Retrieve the property name. - * - * @return string - */ - public function getPropertyName() - { - return $this->propertyName; - } - - /** - * Set the flag if the comparison shall be strict (type safe). - * - * @param boolean $strict The flag. - * - * @return PropertyFalseCondition - */ - public function setStrict($strict) - { - $this->strict = (bool) $strict; - - return $this; - } - - /** - * Retrieve the flag if the comparison shall be strict (type safe). - * - * @return boolean - * - * @SuppressWarnings(PHPMD.BooleanGetMethodName) - */ - public function getStrict() - { - return $this->strict; - } + use PropertyBooleanConditionTrait; /** * {@inheritdoc} @@ -128,11 +55,4 @@ public function match( return $this->strict ? ($value === false) : !$value; } - - /** - * {@inheritdoc} - */ - public function __clone() - { - } } diff --git a/src/DataDefinition/Palette/Condition/Property/PropertyTrueCondition.php b/src/DataDefinition/Palette/Condition/Property/PropertyTrueCondition.php index 156d8fd5..33616650 100644 --- a/src/DataDefinition/Palette/Condition/Property/PropertyTrueCondition.php +++ b/src/DataDefinition/Palette/Condition/Property/PropertyTrueCondition.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -31,79 +32,7 @@ */ class PropertyTrueCondition implements PropertyConditionInterface { - /** - * The property name. - * - * @var string - */ - protected $propertyName; - - /** - * Use strict compare mode. - * - * @var bool - */ - protected $strict; - - /** - * Create a new instance. - * - * @param string $propertyName The name of the property. - * @param bool $strict Flag if the comparison shall be strict (type safe). - */ - public function __construct($propertyName = '', $strict = false) - { - $this->propertyName = (string) $propertyName; - $this->strict = (bool) $strict; - } - - /** - * Set the property name. - * - * @param string $propertyName The property name. - * - * @return PropertyTrueCondition - */ - public function setPropertyName($propertyName) - { - $this->propertyName = $propertyName; - return $this; - } - - /** - * Retrieve the property name. - * - * @return string - */ - public function getPropertyName() - { - return $this->propertyName; - } - - /** - * Set the flag if the comparison shall be strict (type safe). - * - * @param boolean $strict The flag. - * - * @return PropertyTrueCondition - */ - public function setStrict($strict) - { - $this->strict = $strict; - return $this; - } - - /** - * Retrieve the flag if the comparison shall be strict (type safe). - * - * @return boolean - * - * @SuppressWarnings(PHPMD.BooleanGetMethodName) - */ - public function getStrict() - { - return $this->strict; - } + use PropertyBooleanConditionTrait; /** * {@inheritdoc} @@ -124,11 +53,4 @@ public function match( return $this->strict ? ($value === true) : (bool) $value; } - - /** - * {@inheritdoc} - */ - public function __clone() - { - } } diff --git a/src/DataDefinition/Palette/Condition/Property/PropertyValueCondition.php b/src/DataDefinition/Palette/Condition/Property/PropertyValueCondition.php index e55fb4c0..a37c45dc 100644 --- a/src/DataDefinition/Palette/Condition/Property/PropertyValueCondition.php +++ b/src/DataDefinition/Palette/Condition/Property/PropertyValueCondition.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -61,9 +62,9 @@ class PropertyValueCondition implements PropertyConditionInterface */ public function __construct($propertyName = '', $propertyValue = null, $strict = false) { - $this->propertyName = (string) $propertyName; + $this->propertyName = $propertyName; $this->propertyValue = $propertyValue; - $this->strict = (bool) $strict; + $this->strict = $strict; } /** @@ -75,7 +76,8 @@ public function __construct($propertyName = '', $propertyValue = null, $strict = */ public function setPropertyName($propertyName) { - $this->propertyName = (string) $propertyName; + $this->propertyName = $propertyName; + return $this; } @@ -99,6 +101,7 @@ public function getPropertyName() public function setPropertyValue($propertyValue) { $this->propertyValue = $propertyValue; + return $this; } @@ -121,7 +124,8 @@ public function getPropertyValue() */ public function setStrict($strict) { - $this->strict = (bool) $strict; + $this->strict = $strict; + return $this; } diff --git a/src/DataDefinition/Palette/Condition/Property/PropertyVisibleCondition.php b/src/DataDefinition/Palette/Condition/Property/PropertyVisibleCondition.php index ebcba4dc..0f0ba035 100644 --- a/src/DataDefinition/Palette/Condition/Property/PropertyVisibleCondition.php +++ b/src/DataDefinition/Palette/Condition/Property/PropertyVisibleCondition.php @@ -45,7 +45,7 @@ class PropertyVisibleCondition implements PropertyConditionInterface */ public function __construct($propertyName = '') { - $this->propertyName = (string) $propertyName; + $this->propertyName = $propertyName; } /** @@ -57,7 +57,7 @@ public function __construct($propertyName = '') */ public function setPropertyName($propertyName) { - $this->propertyName = (string) $propertyName; + $this->propertyName = $propertyName; return $this; } @@ -84,8 +84,8 @@ public function match( return false; } - if ($legend->getPalette()) { - return $legend->getPalette()->getProperty($this->propertyName)->isVisible($model, $input, $legend); + if (null !== ($palette = $legend->getPalette())) { + return $palette->getProperty($this->propertyName)->isVisible($model, $input, $legend); } return $legend->getProperty($this->propertyName)->isVisible($model, $input, $legend); diff --git a/src/DataDefinition/Palette/Legend.php b/src/DataDefinition/Palette/Legend.php index 2d96ce05..3f4378e0 100644 --- a/src/DataDefinition/Palette/Legend.php +++ b/src/DataDefinition/Palette/Legend.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -22,6 +23,7 @@ namespace ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette; use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; +use ContaoCommunityAlliance\DcGeneral\Data\PropertyValueBag; use ContaoCommunityAlliance\DcGeneral\Data\PropertyValueBagInterface; use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralInvalidArgumentException; use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralRuntimeException; @@ -36,7 +38,7 @@ class Legend implements LegendInterface * * @var PaletteInterface|null */ - protected $palette; + protected $palette = null; /** * The name of this legend. @@ -55,7 +57,7 @@ class Legend implements LegendInterface /** * The properties in this legend. * - * @var PropertyInterface[]|array + * @var array */ protected $properties = []; @@ -95,7 +97,7 @@ public function getPalette() */ public function setName($name) { - $this->name = (string) $name; + $this->name = $name; return $this; } @@ -112,7 +114,7 @@ public function getName() */ public function setInitialVisibility($value) { - $this->initiallyVisible = (bool) $value; + $this->initiallyVisible = $value; return $this; } @@ -176,13 +178,15 @@ public function addProperty(PropertyInterface $property, PropertyInterface $befo ); } - $hashes = \array_keys($this->properties); - $position = \array_search($beforeHash, $hashes); + $hashes = \array_keys($this->properties); + if (false === ($position = \array_search($beforeHash, $hashes))) { + $position = null; + } $this->properties = \array_merge( \array_slice($this->properties, 0, $position), [$hash => $property], - \array_slice($this->properties, $position) + \array_slice($this->properties, $position ?? 0) ); return $this; @@ -212,7 +216,8 @@ public function getProperties(?ModelInterface $model = null, ?PropertyValueBagIn foreach ($this->properties as $property) { $condition = $property->getVisibleCondition(); - + // We should have defined the interfaces back in 2013... :/ + assert($input === null || $input instanceof PropertyValueBag); if (!$condition || $condition->match($model, $input, $property, $this)) { $selectedProperties[] = $property; } diff --git a/src/DataDefinition/Palette/Palette.php b/src/DataDefinition/Palette/Palette.php index 3f2f6115..bb559645 100644 --- a/src/DataDefinition/Palette/Palette.php +++ b/src/DataDefinition/Palette/Palette.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -37,12 +38,12 @@ class Palette implements PaletteInterface * * @var string */ - protected $name; + protected $name = ''; /** * List of all legends in this palette. * - * @var array|LegendInterface[] + * @var array */ protected $legends = []; @@ -58,7 +59,7 @@ class Palette implements PaletteInterface */ public function setName($name) { - $this->name = (string) $name; + $this->name = $name; return $this; } @@ -220,9 +221,9 @@ public function addLegend(LegendInterface $legend, LegendInterface $before = nul $position = \array_search($beforeHash, $hashes); $this->legends = \array_merge( - \array_slice($this->legends, 0, $position), + \array_slice($this->legends, 0, (int) $position), [$hash => $legend], - \array_slice($this->legends, $position) + \array_slice($this->legends, (int) $position) ); $legend->setPalette($this); diff --git a/src/DataDefinition/Palette/PaletteCollection.php b/src/DataDefinition/Palette/PaletteCollection.php index 9ee1b080..7227a9e8 100644 --- a/src/DataDefinition/Palette/PaletteCollection.php +++ b/src/DataDefinition/Palette/PaletteCollection.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2020 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2020 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -22,6 +23,7 @@ namespace ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette; use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; +use ContaoCommunityAlliance\DcGeneral\Data\PropertyValueBag; use ContaoCommunityAlliance\DcGeneral\Data\PropertyValueBagInterface; use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralInvalidArgumentException; @@ -33,7 +35,7 @@ class PaletteCollection implements PaletteCollectionInterface /** * The palettes contained in the collection. * - * @var array|PaletteInterface[] + * @var array */ protected $palettes = []; @@ -117,6 +119,8 @@ public function findPalette(ModelInterface $model = null, PropertyValueBagInterf $condition = $palette->getCondition(); if ($condition) { + // We should have defined the interfaces back in 2013... :/ + assert($input === null || $input instanceof PropertyValueBag); $count = $condition->getMatchCount($model, $input); if (false !== $count) { diff --git a/src/DataDefinition/Palette/PaletteCollectionInterface.php b/src/DataDefinition/Palette/PaletteCollectionInterface.php index cbe4cea7..a499b0a1 100644 --- a/src/DataDefinition/Palette/PaletteCollectionInterface.php +++ b/src/DataDefinition/Palette/PaletteCollectionInterface.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -22,7 +23,6 @@ namespace ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette; use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; -use ContaoCommunityAlliance\DcGeneral\Data\PropertyValueBag; use ContaoCommunityAlliance\DcGeneral\Data\PropertyValueBagInterface; /** @@ -33,7 +33,7 @@ interface PaletteCollectionInterface /** * Remove all palettes from this collection. * - * @return PaletteCollectionInterface + * @return self */ public function clearPalettes(); @@ -42,7 +42,7 @@ public function clearPalettes(); * * @param array|PaletteInterface[] $palettes The palettes. * - * @return PaletteCollectionInterface + * @return self */ public function setPalettes(array $palettes); @@ -51,7 +51,7 @@ public function setPalettes(array $palettes); * * @param array|PaletteInterface[] $palettes The palettes. * - * @return PaletteInterface + * @return self */ public function addPalettes(array $palettes); @@ -60,7 +60,7 @@ public function addPalettes(array $palettes); * * @param PaletteInterface $palette The palette. * - * @return PaletteInterface + * @return self */ public function addPalette(PaletteInterface $palette); @@ -69,7 +69,7 @@ public function addPalette(PaletteInterface $palette); * * @param PaletteInterface $palette The palette. * - * @return PaletteInterface + * @return self */ public function removePalette(PaletteInterface $palette); diff --git a/src/DataDefinition/Palette/Property.php b/src/DataDefinition/Palette/Property.php index 74ec4556..3b00cc9a 100644 --- a/src/DataDefinition/Palette/Property.php +++ b/src/DataDefinition/Palette/Property.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -23,6 +24,7 @@ use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; use ContaoCommunityAlliance\DcGeneral\Data\PropertyValueBag; +use ContaoCommunityAlliance\DcGeneral\Data\PropertyValueBagInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Condition\Property\PropertyConditionInterface; /** @@ -40,16 +42,16 @@ class Property implements PropertyInterface /** * The condition to be examined to determine if this property is visible. * - * @var PropertyConditionInterface + * @var PropertyConditionInterface|null */ - protected $visibleCondition; + protected $visibleCondition = null; /** * The condition to be examined to determine if this property is editable. * - * @var PropertyConditionInterface + * @var PropertyConditionInterface|null */ - protected $editableCondition; + protected $editableCondition = null; /** * Create a new instance. @@ -66,7 +68,9 @@ public function __construct($name) */ public function setName($name) { - $this->name = (string) $name; + $this->name = $name; + + return $this; } /** @@ -82,10 +86,12 @@ public function getName() */ public function isVisible( ModelInterface $model = null, - PropertyValueBag $input = null, + PropertyValueBagInterface $input = null, LegendInterface $legend = null ) { if ($this->visibleCondition) { + // We should have defined the interfaces back in 2013... :/ + assert($input === null || $input instanceof PropertyValueBag); return $this->visibleCondition->match($model, $input, $this, $legend); } @@ -97,10 +103,12 @@ public function isVisible( */ public function isEditable( ModelInterface $model = null, - PropertyValueBag $input = null, + PropertyValueBagInterface $input = null, LegendInterface $legend = null ) { if ($this->editableCondition) { + // We should have defined the interfaces back in 2013... :/ + assert($input === null || $input instanceof PropertyValueBag); return $this->editableCondition->match($model, $input, $this, $legend); } diff --git a/src/DataDefinition/Palette/PropertyInterface.php b/src/DataDefinition/Palette/PropertyInterface.php index 82896db7..e88b3bd2 100644 --- a/src/DataDefinition/Palette/PropertyInterface.php +++ b/src/DataDefinition/Palette/PropertyInterface.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -22,7 +23,7 @@ namespace ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette; use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; -use ContaoCommunityAlliance\DcGeneral\Data\PropertyValueBag; +use ContaoCommunityAlliance\DcGeneral\Data\PropertyValueBagInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Condition\Property\PropertyConditionInterface; /** @@ -49,38 +50,40 @@ public function getName(); /** * Check the conditions, if this property is visible. * - * @param ModelInterface|null $model If given, sub palettes will be evaluated depending on the model. - * If no model is given, all properties will be returned, including sub palette - * properties. - * @param PropertyValueBag $input If given, sub palettes will be evaluated depending on the input data. - * If no model and no input data is given, all properties will be returned, - * including sub palette properties. - * @param LegendInterface $legend The legend the property is assigned to. + * @param ModelInterface|null $model If given, sub palettes will be evaluated depending on the model. + * If no model is given, all properties will be returned, including + * sub palette properties. + * @param PropertyValueBagInterface|null $input If given, sub palettes will be evaluated depending on the input + * data. + * If no model and no input data is given, all properties will be + * returned, including sub palette properties. + * @param LegendInterface|null $legend The legend the property is assigned to. * * @return bool */ public function isVisible( ModelInterface $model = null, - PropertyValueBag $input = null, + PropertyValueBagInterface $input = null, LegendInterface $legend = null ); /** * Check the conditions, if this property is editable. * - * @param ModelInterface|null $model If given, sub palettes will be evaluated depending on the model. - * If no model is given, all properties will be returned, including sub palette - * properties. - * @param PropertyValueBag $input If given, sub palettes will be evaluated depending on the input data. - * If no model and no input data is given, all properties will be returned, - * including sub palette properties. - * @param LegendInterface $legend The legend the property is assigned to. + * @param ModelInterface|null $model If given, sub palettes will be evaluated depending on the model. + * If no model is given, all properties will be returned, including + * sub palette properties. + * @param PropertyValueBagInterface|null $input If given, sub palettes will be evaluated depending on the input + * data. + * If no model and no input data is given, all properties will be + * returned, including sub palette properties. + * @param LegendInterface $legend The legend the property is assigned to. * * @return bool */ public function isEditable( ModelInterface $model = null, - PropertyValueBag $input = null, + PropertyValueBagInterface $input = null, LegendInterface $legend = null ); @@ -96,7 +99,7 @@ public function setVisibleCondition(PropertyConditionInterface $condition = null /** * Get the visible condition for this property. * - * @return PropertyConditionInterface + * @return PropertyConditionInterface|null */ public function getVisibleCondition(); @@ -112,7 +115,7 @@ public function setEditableCondition(PropertyConditionInterface $condition = nul /** * Get the editable condition for this property. * - * @return PropertyConditionInterface + * @return PropertyConditionInterface|null */ public function getEditableCondition(); diff --git a/src/DataDefinitionContainer.php b/src/DataDefinitionContainer.php index e2fb1676..b45c1bba 100644 --- a/src/DataDefinitionContainer.php +++ b/src/DataDefinitionContainer.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,8 @@ * @package contao-community-alliance/dc-general * @author Christian Schiffler * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -33,16 +34,16 @@ class DataDefinitionContainer implements DataDefinitionContainerInterface /** * The definitions stored in the container. * - * @var ContainerInterface[] + * @var array */ - protected $definitions; + protected $definitions = []; /** * {@inheritDoc} */ public function setDefinition($name, $definition) { - if ($definition) { + if (null !== $definition) { $this->definitions[$name] = $definition; } else { unset($this->definitions[$name]); diff --git a/src/DataDefinitionContainerInterface.php b/src/DataDefinitionContainerInterface.php index 28861f7f..bd8e98d7 100644 --- a/src/DataDefinitionContainerInterface.php +++ b/src/DataDefinitionContainerInterface.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,8 @@ * @package contao-community-alliance/dc-general * @author Christian Schiffler * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -32,8 +33,8 @@ interface DataDefinitionContainerInterface /** * Add or override a definition in the container. * - * @param string $name Name of the definition. - * @param ContainerInterface $definition The definition to store. + * @param string $name Name of the definition. + * @param ContainerInterface|null $definition The definition to store - if null, the definition will get unset. * * @return DataDefinitionContainerInterface */ diff --git a/src/DefaultEnvironment.php b/src/DefaultEnvironment.php index b8e133ae..8dc39472 100644 --- a/src/DefaultEnvironment.php +++ b/src/DefaultEnvironment.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -271,7 +272,11 @@ public function getBaseConfigRegistry() public function hasDataProvider($source = null) { if (null === $source) { - $source = $this->getDataDefinition()->getBasicDefinition()->getDataProvider(); + $definition = $this->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $source = $definition->getBasicDefinition()->getDataProvider(); + assert(\is_string($source)); } return isset($this->arrDataProvider[$source]); @@ -282,28 +287,32 @@ public function hasDataProvider($source = null) * * @throws DcGeneralRuntimeException When an undefined provider is requested. */ - public function getDataProvider($source = null) + public function getDataProvider($strSource = null) { - if (null === $source) { - $source = $this->getDataDefinition()->getBasicDefinition()->getDataProvider(); + if (null === $strSource) { + $definition = $this->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $strSource = $definition->getBasicDefinition()->getDataProvider(); + assert(\is_string($strSource)); } - if (isset($this->arrDataProvider[$source])) { - return $this->arrDataProvider[$source]; + if (isset($this->arrDataProvider[$strSource])) { + return $this->arrDataProvider[$strSource]; } - throw new DcGeneralRuntimeException(\sprintf('Data provider %s not defined', $source)); + throw new DcGeneralRuntimeException(\sprintf('Data provider %s not defined', $strSource)); } /** * {@inheritdoc} */ - public function addDataProvider($source, $dataProvider) + public function addDataProvider($strSource, $dataProvider) { // Force removal of an potentially registered data provider to ease sub-classing. - $this->removeDataProvider($source); + $this->removeDataProvider($strSource); - $this->arrDataProvider[$source] = $dataProvider; + $this->arrDataProvider[$strSource] = $dataProvider; return $this; } @@ -311,10 +320,10 @@ public function addDataProvider($source, $dataProvider) /** * {@inheritdoc} */ - public function removeDataProvider($source) + public function removeDataProvider($strSource) { - if (isset($this->arrDataProvider[$source])) { - unset($this->arrDataProvider[$source]); + if (isset($this->arrDataProvider[$strSource])) { + unset($this->arrDataProvider[$strSource]); } return $this; @@ -331,13 +340,9 @@ public function getClipboard() /** * {@inheritdoc} */ - public function setClipboard($clipboard) + public function setClipboard($objClipboard) { - if (null === $clipboard) { - $this->objClipboard = null; - } else { - $this->objClipboard = $clipboard; - } + $this->objClipboard = $objClipboard; return $this; } @@ -345,13 +350,14 @@ public function setClipboard($clipboard) /** * {@inheritdoc} */ - public function setTranslator(TranslatorInterface $translator) + public function setTranslator(TranslatorInterface $manager) { - $this->translator = $translator; + $this->translator = $manager; return $this; } + /** * {@inheritdoc} */ diff --git a/src/DependencyInjection/CcaDcGeneralExtension.php b/src/DependencyInjection/CcaDcGeneralExtension.php index 21fc22bd..3c7b1d29 100644 --- a/src/DependencyInjection/CcaDcGeneralExtension.php +++ b/src/DependencyInjection/CcaDcGeneralExtension.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,8 @@ * @package contao-community-alliance/dc-general * @author Sven Baumann * @author Christian Schiffler - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -56,7 +57,7 @@ class CcaDcGeneralExtension extends Extension /** * {@inheritDoc} */ - public function load(array $configs, ContainerBuilder $container) + public function load(array $configs, ContainerBuilder $container): void { $loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config')); foreach ($this->files as $file) { diff --git a/src/DependencyInjection/Compiler/AddSessionBagsPass.php b/src/DependencyInjection/Compiler/AddSessionBagsPass.php index 6e4a44e6..e4d9c1d5 100644 --- a/src/DependencyInjection/Compiler/AddSessionBagsPass.php +++ b/src/DependencyInjection/Compiler/AddSessionBagsPass.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -12,7 +12,8 @@ * * @package contao-community-alliance/dc-general * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -31,7 +32,7 @@ class AddSessionBagsPass implements CompilerPassInterface /** * {@inheritdoc} */ - public function process(ContainerBuilder $container) + public function process(ContainerBuilder $container): void { if (!$container->has('session')) { return; diff --git a/src/EnvironmentInterface.php b/src/EnvironmentInterface.php index 32d0e9fd..bab50793 100644 --- a/src/EnvironmentInterface.php +++ b/src/EnvironmentInterface.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -41,11 +42,11 @@ interface EnvironmentInterface /** * Set the Controller for the current setup. * - * @param ControllerInterface $objController The controller to use. + * @param ControllerInterface $controller The controller to use. * * @return EnvironmentInterface */ - public function setController($objController); + public function setController($controller); /** * Retrieve the Controller from the current setup. @@ -57,11 +58,11 @@ public function getController(); /** * Set the View for the current setup. * - * @param ViewInterface $objView The view to use. + * @param ViewInterface $view The view to use. * * @return EnvironmentInterface */ - public function setView($objView); + public function setView($view); /** * Retrieve the View from the current setup. @@ -73,11 +74,11 @@ public function getView(); /** * Set the data definition for this instance. * - * @param ContainerInterface $objContainer The data definition container to store. + * @param ContainerInterface $dataDefinition The data definition container to store. * * @return EnvironmentInterface */ - public function setDataDefinition($objContainer); + public function setDataDefinition($dataDefinition); /** * Retrieve the data definition for this instance. @@ -89,11 +90,13 @@ public function getDataDefinition(); /** * Set the data definition of the parent container. * - * @param ContainerInterface $objContainer The data definition container to store. + * @param ContainerInterface $objParentDataDefinition The data definition container to store. * * @return EnvironmentInterface + * + * @SuppressWarnings(PHPMD.LongVariable) */ - public function setParentDataDefinition($objContainer); + public function setParentDataDefinition($objParentDataDefinition); /** * Retrieve the data definition for the parent container. This applies only when in parented mode. @@ -105,11 +108,11 @@ public function getParentDataDefinition(); /** * Set the data definition of the root container. * - * @param ContainerInterface $objContainer The data definition container to store. + * @param ContainerInterface $rootDataDefinition The data definition container to store. * * @return EnvironmentInterface */ - public function setRootDataDefinition($objContainer); + public function setRootDataDefinition($rootDataDefinition); /** * Retrieve the data definition for the root container. This applies only when in hierarchical mode. @@ -137,11 +140,11 @@ public function getSessionStorage(); /** * Set the input provider to use. * - * @param InputProviderInterface $objInputProvider The input provider to use. + * @param InputProviderInterface $inputProvider The input provider to use. * * @return EnvironmentInterface */ - public function setInputProvider($objInputProvider); + public function setInputProvider($inputProvider); /** * Retrieve the input provider. @@ -169,11 +172,11 @@ public function getBaseConfigRegistry(); /** * Determine if the data provider with the given name exists. * - * @param string|null $strSource The source name to check the providers for. + * @param string|null $source The source name to check the providers for. * * @return mixed */ - public function hasDataProvider($strSource = null); + public function hasDataProvider($source = null); /** * Retrieve the data provider for the named source. @@ -214,9 +217,9 @@ public function removeDataProvider($strSource); public function getClipboard(); /** - * Set the the clipboard. + * Set the clipboard. * - * @param ClipboardInterface $objClipboard Clipboard instance. + * @param ClipboardInterface|null $objClipboard Clipboard instance. * * @return EnvironmentInterface */ diff --git a/src/EnvironmentPopulator/AbstractEventDrivenEnvironmentPopulator.php b/src/EnvironmentPopulator/AbstractEventDrivenEnvironmentPopulator.php index cd169618..2c513f91 100644 --- a/src/EnvironmentPopulator/AbstractEventDrivenEnvironmentPopulator.php +++ b/src/EnvironmentPopulator/AbstractEventDrivenEnvironmentPopulator.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,8 @@ * @package contao-community-alliance/dc-general * @author Christian Schiffler * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -50,8 +51,9 @@ abstract class AbstractEventDrivenEnvironmentPopulator implements EnvironmentPop */ public static function process(PopulateEnvironmentEvent $event) { + /** @psalm-suppress UnsafeInstantiation */ $builder = new static(); - /** @var $builder EnvironmentPopulatorInterface */ + /** @var EnvironmentPopulatorInterface $builder */ $builder->populate($event->getEnvironment()); } } diff --git a/src/EventListener/ModelRelationship/ParentEnforcingListener.php b/src/EventListener/ModelRelationship/ParentEnforcingListener.php index 59e0d5df..b35bc3f1 100644 --- a/src/EventListener/ModelRelationship/ParentEnforcingListener.php +++ b/src/EventListener/ModelRelationship/ParentEnforcingListener.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,8 @@ * @package contao-community-alliance/dc-general * @author Christian Schiffler * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -23,8 +24,11 @@ use ContaoCommunityAlliance\DcGeneral\Controller\ModelCollector; use ContaoCommunityAlliance\DcGeneral\Controller\RelationshipManager; use ContaoCommunityAlliance\DcGeneral\Data\ModelId; +use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\BasicDefinitionInterface; use ContaoCommunityAlliance\DcGeneral\Event\EnforceModelRelationshipEvent; +use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; /** * This class takes care of enforcing a parent child relationship on a model. @@ -41,9 +45,14 @@ class ParentEnforcingListener public function process(EnforceModelRelationshipEvent $event) { $environment = $event->getEnvironment(); + $definition = $environment->getDataDefinition(); - $mode = $definition->getBasicDefinition()->getMode(); - $input = $environment->getInputProvider(); + assert($definition instanceof ContainerInterface); + + $mode = $definition->getBasicDefinition()->getMode(); + + $input = $environment->getInputProvider(); + assert($input instanceof InputProviderInterface); if (BasicDefinitionInterface::MODE_PARENTEDLIST !== $mode || !$input->hasParameter('pid')) { return; @@ -52,6 +61,7 @@ public function process(EnforceModelRelationshipEvent $event) $model = $event->getModel(); $parent = (new ModelCollector($environment))->getModel(ModelId::fromSerialized($input->getParameter('pid'))); + assert($parent instanceof ModelInterface); (new RelationshipManager($definition->getModelRelationshipDefinition(), $mode))->setParent($model, $parent); } diff --git a/src/EventListener/ModelRelationship/TreeEnforcingListener.php b/src/EventListener/ModelRelationship/TreeEnforcingListener.php index 3fae9e25..12cccae2 100644 --- a/src/EventListener/ModelRelationship/TreeEnforcingListener.php +++ b/src/EventListener/ModelRelationship/TreeEnforcingListener.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Sven Baumann * @author Richard Henkenjohann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -24,9 +25,12 @@ use ContaoCommunityAlliance\DcGeneral\Controller\ModelCollector; use ContaoCommunityAlliance\DcGeneral\Controller\RelationshipManager; use ContaoCommunityAlliance\DcGeneral\Data\ModelId; +use ContaoCommunityAlliance\DcGeneral\Data\ModelIdInterface; use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\BasicDefinitionInterface; use ContaoCommunityAlliance\DcGeneral\Event\EnforceModelRelationshipEvent; +use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; /** * This class takes care of enforcing a tree relationship on a model. @@ -43,17 +47,23 @@ class TreeEnforcingListener public function process(EnforceModelRelationshipEvent $event) { $environment = $event->getEnvironment(); - $mode = $environment->getDataDefinition()->getBasicDefinition()->getMode(); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $mode = $definition->getBasicDefinition()->getMode(); if (BasicDefinitionInterface::MODE_HIERARCHICAL !== $mode) { return; } - $input = $environment->getInputProvider(); + $input = $environment->getInputProvider(); + assert($input instanceof InputProviderInterface); + $model = $event->getModel(); $collector = new ModelCollector($environment); $relationships = new RelationshipManager( - $environment->getDataDefinition()->getModelRelationshipDefinition(), + $definition->getModelRelationshipDefinition(), $mode ); @@ -76,6 +86,7 @@ public function process(EnforceModelRelationshipEvent $event) // Also enforce the parent condition of the parent provider (if any). if ($input->hasParameter('pid')) { $parent = $collector->getModel($input->getParameter('pid')); + assert($parent instanceof ModelInterface); $relationships->setParent($model, $parent); } } @@ -83,7 +94,7 @@ public function process(EnforceModelRelationshipEvent $event) /** * Handle paste into. * - * @param ModelId $into The id of the new parenting model. + * @param ModelIdInterface $into The id of the new parenting model. * @param RelationshipManager $relationships The relationship manager. * @param ModelCollector $collector The model collector. * @param ModelInterface $model The model. @@ -91,11 +102,11 @@ public function process(EnforceModelRelationshipEvent $event) * @return void */ private function handleInto( - ModelId $into, + ModelIdInterface $into, RelationshipManager $relationships, ModelCollector $collector, ModelInterface $model - ) { + ): void { // If we have a null, it means insert into the tree root. if (0 === $into->getId() || null === $parent = $collector->getModel($into)) { $relationships->setRoot($model); @@ -108,7 +119,7 @@ private function handleInto( /** * Handle paste after. * - * @param ModelId $after The id of the sibling model. + * @param ModelIdInterface $after The id of the sibling model. * @param RelationshipManager $relationships The relationship manager. * @param ModelCollector $collector The model collector. * @param ModelInterface $model The model. @@ -116,7 +127,7 @@ private function handleInto( * @return void */ private function handleAfter( - ModelId $after, + ModelIdInterface $after, RelationshipManager $relationships, ModelCollector $collector, ModelInterface $model @@ -129,6 +140,8 @@ private function handleAfter( } $parent = $collector->searchParentOf($sibling); + assert($parent instanceof ModelInterface); + $relationships->setParent($model, $parent); } } diff --git a/src/Factory/DcGeneralFactory.php b/src/Factory/DcGeneralFactory.php index 1fe0e6f7..519fd3df 100644 --- a/src/Factory/DcGeneralFactory.php +++ b/src/Factory/DcGeneralFactory.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2022 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -15,7 +15,7 @@ * @author Tristan Lins * @author Sven Baumann * @author Ingolf Steinhardt - * @copyright 2013-2022 Contao Community Alliance. + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -49,9 +49,9 @@ class DcGeneralFactory implements DcGeneralFactoryInterface /** * The cache. * - * @var CacheInterface|null + * @var CacheInterface */ - private $cache; + private CacheInterface $cache; /** * The constructor. @@ -60,16 +60,19 @@ class DcGeneralFactory implements DcGeneralFactoryInterface */ public function __construct(CacheInterface $cache = null) { - $this->cache = $cache; - if (null === $this->cache) { + if (null === $cache) { // @codingStandardsIgnoreStart @\trigger_error( - 'You should use ' . DcGeneralFactoryCache::class . ' .', + 'You should pass an instance of ' . CacheInterface::class . ' .', E_USER_DEPRECATED ); // @codingStandardsIgnoreEnd - $this->cache = System::getContainer()->get(DcGeneralFactoryCache::class); + /** @psalm-suppress DeprecatedClass */ + $cache = System::getContainer()->get(DcGeneralFactoryCache::class); + + assert($cache instanceof CacheInterface); } + $this->cache = $cache; } /** @@ -84,11 +87,20 @@ public function __construct(CacheInterface $cache = null) */ public static function deriveEmptyFromEnvironment(EnvironmentInterface $environment) { + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $translator = $environment->getTranslator(); + assert($translator instanceof TranslatorInterface); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + $factory = new DcGeneralFactory(); - $factory->setEventDispatcher($environment->getEventDispatcher()); - $factory->setTranslator($environment->getTranslator()); + $factory->setEventDispatcher($dispatcher); + $factory->setTranslator($translator); $factory->setEnvironmentClassName(\get_class($environment)); - $factory->setContainerClassName(\get_class($environment->getDataDefinition())); + $factory->setContainerClassName(\get_class($definition)); return $factory; } @@ -104,14 +116,18 @@ public static function deriveEmptyFromEnvironment(EnvironmentInterface $environm public static function deriveFromEnvironment(EnvironmentInterface $environment) { $factory = static::deriveEmptyFromEnvironment($environment); - $factory->setContainerName($environment->getDataDefinition()->getName()); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $factory->setContainerName($definition->getName()); return $factory; } /** * The class name to use for the environment. * - * @var string + * @var class-string */ protected $environmentClassName = DefaultEnvironment::class; @@ -120,56 +136,56 @@ public static function deriveFromEnvironment(EnvironmentInterface $environment) * * @var string */ - protected $containerName; + protected $containerName = ''; /** * The class name of the class to use for the data definition container. * - * @var string + * @var class-string */ protected $containerClassName = DefaultContainer::class; /** * The class name of the class to use as DcGeneral. * - * @var string + * @var class-string */ protected $dcGeneralClassName = DcGeneral::class; /** * The event dispatcher to use. * - * @var EventDispatcherInterface + * @var EventDispatcherInterface|null */ - protected $eventDispatcher; + protected $eventDispatcher = null; /** * The translator that shall be used. * - * @var TranslatorInterface + * @var TranslatorInterface|null */ - protected $translator; + protected $translator = null; /** * The environment for the new instance. * - * @var EnvironmentInterface + * @var EnvironmentInterface|null */ - protected $environment; + protected $environment = null; /** * The data definition container instance. * - * @var ContainerInterface + * @var ContainerInterface|null */ - protected $dataContainer; + protected $dataContainer = null; /** * {@inheritdoc} */ public function setEnvironmentClassName($environmentClassName) { - $this->environmentClassName = (string) $environmentClassName; + $this->environmentClassName = $environmentClassName; return $this; } @@ -187,7 +203,7 @@ public function getEnvironmentClassName() */ public function setContainerName($containerName) { - $this->containerName = (string) $containerName; + $this->containerName = $containerName; return $this; } @@ -205,7 +221,7 @@ public function getContainerName() */ public function setContainerClassName($containerClassName) { - $this->containerClassName = (string) $containerClassName; + $this->containerClassName = $containerClassName; return $this; } @@ -223,7 +239,7 @@ public function getContainerClassName() */ public function setDcGeneralClassName($dcGeneralClassName) { - $this->dcGeneralClassName = (string) $dcGeneralClassName; + $this->dcGeneralClassName = $dcGeneralClassName; return $this; } @@ -251,6 +267,10 @@ public function setEventDispatcher($dispatcher) */ public function getEventDispatcher() { + if (null === $this->eventDispatcher) { + throw new DcGeneralRuntimeException('Required event dispatcher is missing'); + } + return $this->eventDispatcher; } @@ -269,6 +289,10 @@ public function setTranslator(TranslatorInterface $translator) */ public function getTranslator() { + if (null === $this->translator) { + throw new DcGeneralRuntimeException('Required translator is missing'); + } + return $this->translator; } @@ -324,13 +348,15 @@ public function createDcGeneral() } $cacheKey = \md5('dc-general.' . $this->containerName); - return $this->cache->get($cacheKey, function (): DcGeneral { + $cache = $this->cache; + assert($cache instanceof CacheInterface); + + return $cache->get($cacheKey, function (): DcGeneral { // Backwards compatibility. $this->getEventDispatcher()->dispatch(new PreCreateDcGeneralEvent($this), PreCreateDcGeneralEvent::NAME); $environment = $this->environment ?: $this->createEnvironment(); - /** @var DcGeneral $dcGeneral */ $dcGeneral = (new \ReflectionClass($this->dcGeneralClassName))->newInstance($environment); // Backwards compatibility. @@ -352,25 +378,16 @@ public function createEnvironment() throw new DcGeneralRuntimeException('Required container name or container is missing'); } - if (null === $this->eventDispatcher) { - throw new DcGeneralRuntimeException('Required event dispatcher is missing'); - } - - if (null === $this->translator) { - throw new DcGeneralRuntimeException('Required translator is missing'); - } - if ($this->dataContainer) { $dataContainer = clone $this->dataContainer; } else { $dataContainer = $this->createContainer(); } - /** @var EnvironmentInterface $environment */ $environment = (new \ReflectionClass($this->environmentClassName))->newInstance(); $environment->setDataDefinition($dataContainer); - $environment->setEventDispatcher($this->eventDispatcher); - $environment->setTranslator($this->translator); + $environment->setEventDispatcher($this->getEventDispatcher()); + $environment->setTranslator($this->getTranslator()); // Backwards compatibility. $this->getEventDispatcher()->dispatch( @@ -395,12 +412,9 @@ public function createContainer() throw new DcGeneralRuntimeException('Required container name is missing'); } - if (null === $this->eventDispatcher) { - throw new DcGeneralRuntimeException('Required event dispatcher is missing'); - } - /** @var DataDefinitionContainerInterface $definitions */ $definitions = System::getContainer()->get('cca.dc-general.data-definition-container'); + assert($definitions instanceof DataDefinitionContainerInterface); if ($definitions->hasDefinition($this->containerName)) { return clone $definitions->getDefinition($this->containerName); diff --git a/src/Factory/DcGeneralFactoryInterface.php b/src/Factory/DcGeneralFactoryInterface.php index 9ee9b345..037d1e3b 100644 --- a/src/Factory/DcGeneralFactoryInterface.php +++ b/src/Factory/DcGeneralFactoryInterface.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -37,7 +38,7 @@ interface DcGeneralFactoryInterface /** * Set the class name to use as environment. * - * @param string $environmentClassName The class name. + * @param class-string $environmentClassName The class name. * * @return DcGeneralFactoryInterface */ @@ -46,7 +47,7 @@ public function setEnvironmentClassName($environmentClassName); /** * Retrieve the class name to use as environment. * - * @return string + * @return class-string */ public function getEnvironmentClassName(); @@ -69,7 +70,7 @@ public function getContainerName(); /** * Set the class name to use as data definition container. * - * @param string $containerClassName The class name. + * @param class-string $containerClassName The class name. * * @return DcGeneralFactoryInterface */ @@ -78,14 +79,14 @@ public function setContainerClassName($containerClassName); /** * Retrieve the class name to use as data definition container. * - * @return string + * @return class-string */ public function getContainerClassName(); /** * Set the class name to use as DcGeneral. * - * @param string $dcGeneralClassName The class name. + * @param class-string $dcGeneralClassName The class name. * * @return DcGeneralFactoryInterface */ @@ -94,7 +95,7 @@ public function setDcGeneralClassName($dcGeneralClassName); /** * Retrieve the class name to use as DcGeneral. * - * @return string + * @return class-string */ public function getDcGeneralClassName(); @@ -133,32 +134,32 @@ public function getTranslator(); /** * Set the environment to use. * - * @param EnvironmentInterface $environment The environment instance. + * @param EnvironmentInterface|null $environment The environment instance. * * @return DcGeneralFactoryInterface */ - public function setEnvironment(EnvironmentInterface $environment = null); + public function setEnvironment(?EnvironmentInterface $environment = null); /** * Retrieve the environment to use. * - * @return EnvironmentInterface + * @return EnvironmentInterface|null */ public function getEnvironment(); /** * Set the data definition container to use. * - * @param ContainerInterface $dataContainer The data definition container instance. + * @param ContainerInterface|null $dataContainer The data definition container instance. * * @return DcGeneralFactoryInterface */ - public function setDataContainer(ContainerInterface $dataContainer = null); + public function setDataContainer(?ContainerInterface $dataContainer = null); /** * Retrieve the data definition container. * - * @return ContainerInterface + * @return ContainerInterface|null */ public function getDataContainer(); diff --git a/src/Panel/AbstractElement.php b/src/Panel/AbstractElement.php index 41ac490f..10ffd98a 100644 --- a/src/Panel/AbstractElement.php +++ b/src/Panel/AbstractElement.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,13 +14,15 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ namespace ContaoCommunityAlliance\DcGeneral\Panel; +use ContaoCommunityAlliance\DcGeneral\BaseConfigRegistryInterface; use ContaoCommunityAlliance\DcGeneral\Data\ConfigInterface; use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; @@ -34,18 +36,18 @@ abstract class AbstractElement implements PanelElementInterface /** * The panel this element is contained within. * - * @var PanelInterface + * @var PanelInterface|null */ - protected $objPanel; + protected $objPanel = null; /** * The base configuration that contains all filter, sorting and limit information for all other panel elements. * * This is used for determining the valid values in filters etc. * - * @var ConfigInterface + * @var ConfigInterface|null */ - private $objOtherConfig; + private $objOtherConfig = null; /** * Convenience method to retrieve Environment for this element. @@ -64,7 +66,10 @@ public function getEnvironment() */ public function getSessionStorage() { - return $this->getEnvironment()->getSessionStorage(); + $sessionStorage = $this->getEnvironment()->getSessionStorage(); + assert($sessionStorage instanceof SessionStorageInterface); + + return $sessionStorage; } /** @@ -74,7 +79,10 @@ public function getSessionStorage() */ public function getInputProvider() { - return $this->getEnvironment()->getInputProvider(); + $inputProvider = $this->getEnvironment()->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + + return $inputProvider; } /** @@ -82,15 +90,19 @@ public function getInputProvider() */ public function getPanel() { + if (null === $this->objPanel) { + throw new \LogicException('Panel not set'); + } + return $this->objPanel; } /** * {@inheritDoc} */ - public function setPanel(PanelInterface $panel) + public function setPanel(PanelInterface $panelElement) { - $this->objPanel = $panel; + $this->objPanel = $panelElement; return $this; } @@ -102,11 +114,11 @@ public function setPanel(PanelInterface $panel) */ protected function getOtherConfig() { - if (!isset($this->objOtherConfig)) { - $this->objOtherConfig = $this - ->getEnvironment() - ->getBaseConfigRegistry() - ->getBaseConfig(); + if (null === $this->objOtherConfig) { + $registry = $this->getEnvironment()->getBaseConfigRegistry(); + assert($registry instanceof BaseConfigRegistryInterface); + + $this->objOtherConfig = $registry->getBaseConfig(); $this ->getPanel() diff --git a/src/Panel/DefaultFilterElement.php b/src/Panel/DefaultFilterElement.php index 9409a03d..2509cc89 100644 --- a/src/Panel/DefaultFilterElement.php +++ b/src/Panel/DefaultFilterElement.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2022 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -17,7 +17,7 @@ * @author Ingolf Steinhardt * @author Sven Baumann * @author Cliff Parnitzky - * @copyright 2013-2022 Contao Community Alliance. + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -25,7 +25,11 @@ namespace ContaoCommunityAlliance\DcGeneral\Panel; use ContaoCommunityAlliance\DcGeneral\Data\ConfigInterface; +use ContaoCommunityAlliance\DcGeneral\Data\DataProviderInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\ModelRelationship\FilterBuilder; +use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; +use ContaoCommunityAlliance\DcGeneral\SessionStorageInterface; use ContaoCommunityAlliance\DcGeneral\View\ViewTemplateInterface; /** @@ -38,7 +42,7 @@ class DefaultFilterElement extends AbstractElement implements FilterElementInter * * @var string */ - private $strProperty; + private $strProperty = 'string'; /** * The current value of this filter. @@ -52,7 +56,7 @@ class DefaultFilterElement extends AbstractElement implements FilterElementInter * * @var array */ - private $arrFilterOptions; + private $arrFilterOptions = []; /** * Retrieve the persistent value from the input provider. @@ -66,8 +70,11 @@ protected function getPersistent() $values = $this->getSessionStorage()->get('filter'); } - if (\array_key_exists($this->getEnvironment()->getDataDefinition()->getName(), $values)) { - $values = $values[$this->getEnvironment()->getDataDefinition()->getName()]; + $definition = $this->getEnvironment()->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + if (\array_key_exists($definition->getName(), $values)) { + $values = $values[$definition->getName()]; if (\array_key_exists($this->getPropertyName(), $values)) { return $values[$this->getPropertyName()]; @@ -86,7 +93,10 @@ protected function getPersistent() */ protected function setPersistent($value) { - $definitionName = $this->getEnvironment()->getDataDefinition()->getName(); + $definition = $this->getEnvironment()->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $definitionName = $definition->getName(); $values = []; @@ -95,7 +105,7 @@ protected function setPersistent($value) } if (isset($values[$definitionName]) && !\is_array($values[$definitionName])) { - $values[$this->getEnvironment()->getDataDefinition()->getName()] = []; + $values[$definition->getName()] = []; } if ((null !== $values) && ($value !== 'tl_' . $this->getPropertyName())) { @@ -115,10 +125,14 @@ protected function setPersistent($value) private function updateValue() { $session = $this->getSessionStorage(); - $input = $this->getInputProvider(); + assert($session instanceof SessionStorageInterface); + + $input = $this->getInputProvider(); + assert($input instanceof InputProviderInterface); + $value = null; - if ('1' !== $this->getEnvironment()->getInputProvider()->getValue('filter_reset')) { + if ('1' !== $input->getValue('filter_reset')) { if ($input->hasValue($this->getPropertyName()) && $this->getPanel()->getContainer()->updateValues()) { $value = $input->getValue($this->getPropertyName()); @@ -148,14 +162,14 @@ private function loadFilterOptions() $otherConfig = $this->getOtherConfig(); $otherConfig->setFields([$this->getPropertyName()]); - $filterOptions = $this - ->getEnvironment() - ->getDataProvider() - ->getFilterOptions($otherConfig); + $dataProvider = $this->getEnvironment()->getDataProvider(); + assert($dataProvider instanceof DataProviderInterface); + + $filterOptions = $dataProvider->getFilterOptions($otherConfig); $options = []; foreach ($filterOptions as $filterKey => $filterValue) { - $options[(string) $filterKey] = $filterValue; + $options[$filterKey] = $filterValue; } $this->arrFilterOptions = $options; @@ -193,10 +207,10 @@ public function initialize(ConfigInterface $config, PanelElementInterface $eleme */ public function render(ViewTemplateInterface $viewTemplate) { - $labels = $this - ->getEnvironment() - ->getDataDefinition() - ->getPropertiesDefinition() + $definition = $this->getEnvironment()->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $labels = $definition->getPropertiesDefinition() ->getProperty($this->getPropertyName())->getLabel(); $options = [ @@ -216,7 +230,7 @@ public function render(ViewTemplateInterface $viewTemplate) ]; } - $viewTemplate->set('label', (\is_array($labels) ? $labels[0] : $labels)); + $viewTemplate->set('label', $labels); $viewTemplate->set('name', $this->getPropertyName()); $viewTemplate->set('id', $this->getPropertyName()); $viewTemplate->set('class', 'tl_select' . ((null !== $selectedValue) ? ' active' : '')); diff --git a/src/Panel/DefaultLimitElement.php b/src/Panel/DefaultLimitElement.php index fb513b21..8d939278 100644 --- a/src/Panel/DefaultLimitElement.php +++ b/src/Panel/DefaultLimitElement.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2022 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -17,7 +17,7 @@ * @author Ingolf Steinhardt * @author Sven Baumann * @author Cliff Parnitzky - * @copyright 2013-2022 Contao Community Alliance. + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -26,6 +26,9 @@ use Contao\Config; use ContaoCommunityAlliance\DcGeneral\Data\ConfigInterface; +use ContaoCommunityAlliance\DcGeneral\Data\DataProviderInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; +use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; use ContaoCommunityAlliance\DcGeneral\View\ViewTemplateInterface; /** @@ -38,21 +41,21 @@ class DefaultLimitElement extends AbstractElement implements LimitElementInterfa * * @var int */ - private $intOffset; + private $intOffset = 0; /** * The current amount. * * @var int */ - private $intAmount; + private $intAmount = 0; /** * The total amount of all valid entries. * * @var int */ - private $intTotal; + private $intTotal = 0; /** * Retrieve the amount of items to display per page. @@ -82,24 +85,19 @@ protected function getMaxItemsPerPage() protected function calculateTotal() { $otherConfig = $this->getOtherConfig(); - $total = $this - ->getEnvironment() - ->getDataProvider() - ->fetchAll($otherConfig->setIdOnly(true)); - if (\is_array($total)) { - $this->intTotal = $total ? \count($total) : 0; + $dataProvider = $this->getEnvironment()->getDataProvider(); + assert($dataProvider instanceof DataProviderInterface); - return; - } + $total = $dataProvider->fetchAll($otherConfig->setIdOnly(true)); - if (\is_object($total)) { - $this->intTotal = $total->length(); + if (\is_array($total)) { + $this->intTotal = $total ? \count($total) : 0; return; } - $this->intTotal = 0; + $this->intTotal = $total->length(); } /** @@ -114,8 +112,11 @@ protected function getPersistent() $values = $this->getSessionStorage()->get('limit'); } - if (\array_key_exists($this->getEnvironment()->getDataDefinition()->getName(), $values)) { - return $values[$this->getEnvironment()->getDataDefinition()->getName()]; + $definition = $this->getEnvironment()->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + if (\array_key_exists($definition->getName(), $values)) { + return $values[$definition->getName()]; } return []; @@ -131,7 +132,10 @@ protected function getPersistent() */ protected function setPersistent($offset, $amount) { - $definitionName = $this->getEnvironment()->getDataDefinition()->getName(); + $definition = $this->getEnvironment()->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $definitionName = $definition->getName(); $values = []; @@ -172,11 +176,6 @@ public function initialize(ConfigInterface $config, PanelElementInterface $eleme $this->defineOffsetAndAmountOption($offset, $amount); - if (null !== $offset) { - $this->setOffset($offset); - $this->setAmount($amount); - } - $config->setStart($this->getOffset()); $config->setAmount($this->getAmount()); } @@ -191,8 +190,11 @@ public function initialize(ConfigInterface $config, PanelElementInterface $eleme */ private function defineOffsetAndAmountOption(int &$offset, int &$amount): void { - if ('1' === $this->getEnvironment()->getInputProvider()->getValue('filter_reset')) { - $this->setPersistent(null, null); + $inputProvider = $this->getEnvironment()->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + + if ('1' === $inputProvider->getValue('filter_reset')) { + $this->setPersistent(0, 0); return; } @@ -200,8 +202,8 @@ private function defineOffsetAndAmountOption(int &$offset, int &$amount): void if ($input->hasValue('tl_limit') && $this->getPanel()->getContainer()->updateValues()) { $limit = $input->getValue('tl_limit'); if ('tl_limit' !== $limit) { - [$offset, $amount] = \explode(',', $input->getValue('tl_limit')) + [null, null]; - $this->setPersistent($offset, $amount); + [$offset, $amount] = \explode(',', $input->getValue('tl_limit')) + [0, 0]; + $this->setPersistent((int) $offset, (int) $amount); } } @@ -295,9 +297,9 @@ public function render(ViewTemplateInterface $viewTemplate) /** * {@inheritDoc} */ - public function setOffset($offset) + public function setOffset($intOffset) { - $this->intOffset = (int) $offset; + $this->intOffset = $intOffset; return $this; } diff --git a/src/Panel/DefaultPanel.php b/src/Panel/DefaultPanel.php index 84506985..2db61511 100644 --- a/src/Panel/DefaultPanel.php +++ b/src/Panel/DefaultPanel.php @@ -32,16 +32,16 @@ class DefaultPanel implements PanelInterface /** * The panel container this panel is contained within. * - * @var PanelContainerInterface + * @var PanelContainerInterface|null */ - private $objContainer; + private ?PanelContainerInterface $objContainer = null; /** * The elements contained within this panel. * - * @var PanelElementInterface[] + * @var array */ - private $arrElements; + private array $arrElements; /** * Create a new instance. @@ -56,6 +56,10 @@ public function __construct() */ public function getContainer() { + if (null === $this->objContainer) { + throw new \LogicException('Container for panel is not set.'); + } + return $this->objContainer; } diff --git a/src/Panel/DefaultPanelContainer.php b/src/Panel/DefaultPanelContainer.php index 71ae322a..0643f846 100644 --- a/src/Panel/DefaultPanelContainer.php +++ b/src/Panel/DefaultPanelContainer.php @@ -24,6 +24,7 @@ use ContaoCommunityAlliance\DcGeneral\Data\ConfigInterface; use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; +use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; /** * Default implementation of a panel container. @@ -33,31 +34,36 @@ class DefaultPanelContainer implements PanelContainerInterface /** * The environment in use. * - * @var EnvironmentInterface + * @var EnvironmentInterface|null */ - private $objEnvironment; + private $objEnvironment = null; /** * The panels contained within this container. * - * @var PanelInterface[] + * @var array */ - private $arrPanels = []; + private array $panels = []; /** * {@inheritdoc} */ public function getEnvironment() { + if (null === $this->objEnvironment) { + throw new \LogicException('Environment for panel is not set.'); + } + return $this->objEnvironment; } /** * {@inheritdoc} */ - public function setEnvironment(EnvironmentInterface $environment) + public function setEnvironment(EnvironmentInterface $objEnvironment) { - $this->objEnvironment = $environment; + $this->objEnvironment = $objEnvironment; + return $this; } @@ -66,7 +72,7 @@ public function setEnvironment(EnvironmentInterface $environment) */ public function addPanel($panelName, $panel) { - $this->arrPanels[$panelName] = $panel; + $this->panels[$panelName] = $panel; $panel->setContainer($this); return $this; @@ -77,7 +83,7 @@ public function addPanel($panelName, $panel) */ public function getPanel($panelName) { - return $this->arrPanels[$panelName]; + return $this->panels[$panelName]; } /** @@ -89,6 +95,8 @@ public function initialize(ConfigInterface $config, PanelElementInterface $eleme foreach ($this as $panel) { $panel->initialize($config, $element); } + + return $this; } /** @@ -96,7 +104,10 @@ public function initialize(ConfigInterface $config, PanelElementInterface $eleme */ public function updateValues() { - return ('tl_filters' === $this->getEnvironment()->getInputProvider()->getValue('FORM_SUBMIT')); + $inputProvider = $this->getEnvironment()->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + + return ('tl_filters' === $inputProvider->getValue('FORM_SUBMIT')); } /** @@ -104,7 +115,7 @@ public function updateValues() */ public function getIterator(): \Traversable { - return new \ArrayIterator($this->arrPanels); + return new \ArrayIterator($this->panels); } /** @@ -112,6 +123,6 @@ public function getIterator(): \Traversable */ public function count(): int { - return \count($this->arrPanels); + return \count($this->panels); } } diff --git a/src/Panel/DefaultSearchElement.php b/src/Panel/DefaultSearchElement.php index 5f11d57a..72f3d2dd 100644 --- a/src/Panel/DefaultSearchElement.php +++ b/src/Panel/DefaultSearchElement.php @@ -24,6 +24,8 @@ namespace ContaoCommunityAlliance\DcGeneral\Panel; use ContaoCommunityAlliance\DcGeneral\Data\ConfigInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; +use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; use ContaoCommunityAlliance\DcGeneral\View\ViewTemplateInterface; /** @@ -36,21 +38,21 @@ class DefaultSearchElement extends AbstractElement implements SearchElementInter * * @var array */ - private $arrProperties; + private array $arrProperties = []; /** * The currently active property to be searched on. * * @var string */ - private $strSelectedProperty; + private string $strSelectedProperty = ''; /** * The current value to be searched. * * @var mixed */ - private $mixValue; + private mixed $mixValue = null; /** * Retrieve the persistent value from the input provider. @@ -59,13 +61,16 @@ class DefaultSearchElement extends AbstractElement implements SearchElementInter */ protected function getPersistent() { + $definition = $this->getEnvironment()->getDataDefinition(); + assert($definition instanceof ContainerInterface); + $values = []; if ($this->getSessionStorage()->has('search')) { $values = $this->getSessionStorage()->get('search'); } - if (\array_key_exists($this->getEnvironment()->getDataDefinition()->getName(), $values)) { - return $values[$this->getEnvironment()->getDataDefinition()->getName()]; + if (\array_key_exists($definition->getName(), $values)) { + return $values[$definition->getName()]; } return []; @@ -81,9 +86,12 @@ protected function getPersistent() */ protected function setPersistent($propertyName, $searchValue) { - $values = []; - $definitionName = $this->getEnvironment()->getDataDefinition()->getName(); + $definition = $this->getEnvironment()->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $definitionName = $definition->getName(); + $values = []; if ($this->getSessionStorage()->has('search')) { $values = $this->getSessionStorage()->get('search'); } @@ -93,12 +101,8 @@ protected function setPersistent($propertyName, $searchValue) $values[$definitionName] = []; } - if ($searchValue) { - $values[$definitionName]['field'] = $propertyName; - $values[$definitionName]['value'] = $searchValue; - } else { - unset($values[$definitionName]); - } + $values[$definitionName]['field'] = $propertyName; + $values[$definitionName]['value'] = $searchValue; } else { unset($values[$definitionName]); } @@ -109,14 +113,16 @@ protected function setPersistent($propertyName, $searchValue) /** * {@inheritdoc} */ - public function initialize(ConfigInterface $filterConfig, PanelElementInterface $objElement = null) + public function initialize(ConfigInterface $config, PanelElementInterface $element = null) { $session = $this->getSessionStorage(); $input = $this->getInputProvider(); - $value = null; - $field = null; + assert($input instanceof InputProviderInterface); + + $value = ''; + $field = ''; - if ('1' !== $this->getEnvironment()->getInputProvider()->getValue('filter_reset')) { + if ('1' !== $input->getValue('filter_reset')) { if ($input->hasValue('tl_field') && $this->getPanel()->getContainer()->updateValues()) { $field = $input->getValue('tl_field'); $value = $input->getValue('tl_value'); @@ -133,19 +139,19 @@ public function initialize(ConfigInterface $filterConfig, PanelElementInterface $this->setSelectedProperty($field); $this->setValue($value); } else { - $this->setPersistent(null, null); + $this->setPersistent('', ''); } if (!($this->getSelectedProperty() && $this->getValue())) { return; } - $currents = $filterConfig->getFilter(); + $currents = $config->getFilter(); if (!\is_array($currents)) { $currents = []; } - $filterConfig->setFilter( + $config->setFilter( \array_merge_recursive( $currents, [ @@ -164,20 +170,21 @@ public function initialize(ConfigInterface $filterConfig, PanelElementInterface */ public function render(ViewTemplateInterface $viewTemplate) { + $definition = $this->getEnvironment()->getDataDefinition(); + assert($definition instanceof ContainerInterface); + $options = []; foreach ($this->getPropertyNames() as $field) { - $lLabels = $this - ->getEnvironment() - ->getDataDefinition() + $lLabels = $definition ->getPropertiesDefinition() ->getProperty($field) ->getLabel(); $options[] = [ - 'value' => $field, - 'content' => \is_array($lLabels) ? $lLabels[0] : $lLabels, - 'attributes' => ($field === $this->getSelectedProperty()) ? ' selected' : '' - ]; + 'value' => $field, + 'content' => $lLabels, + 'attributes' => ($field === $this->getSelectedProperty()) ? ' selected' : '' + ]; } $viewTemplate->set('class', 'tl_select' . (!empty($this->getValue()) ? ' active' : '')); @@ -190,9 +197,9 @@ public function render(ViewTemplateInterface $viewTemplate) /** * {@inheritDoc} */ - public function addProperty($propertyName) + public function addProperty($strProperty) { - $this->arrProperties[] = $propertyName; + $this->arrProperties[] = $strProperty; return $this; } @@ -208,9 +215,9 @@ public function getPropertyNames(): array /** * {@inheritDoc} */ - public function setSelectedProperty($propertyName = '') + public function setSelectedProperty($strProperty = '') { - $this->strSelectedProperty = $propertyName; + $this->strSelectedProperty = $strProperty; return $this; } diff --git a/src/Panel/DefaultSortElement.php b/src/Panel/DefaultSortElement.php index 22346d94..3f5b83a5 100644 --- a/src/Panel/DefaultSortElement.php +++ b/src/Panel/DefaultSortElement.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2022 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -17,7 +17,7 @@ * @author Ingolf Steinhardt * @author Sven Baumann * @author Cliff Parnitzky - * @copyright 2013-2022 Contao Community Alliance. + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -27,10 +27,11 @@ use Contao\StringUtil; use ContaoCommunityAlliance\DcGeneral\Contao\DataDefinition\Definition\Contao2BackendViewDefinitionInterface; use ContaoCommunityAlliance\DcGeneral\Data\ConfigInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\GroupAndSortingDefinitionCollectionInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\GroupAndSortingDefinitionInterface; +use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; use ContaoCommunityAlliance\DcGeneral\View\ViewTemplateInterface; -use phpDocumentor\Reflection\Types\Mixed_; /** * Default implementation of a sort element. @@ -40,21 +41,22 @@ class DefaultSortElement extends AbstractElement implements SortElementInterface /** * The selected definition. * - * @var GroupAndSortingDefinitionInterface + * @var GroupAndSortingDefinitionInterface|null */ - private $selected; + private $selected = null; /** * Retrieve the group and sorting definition. * - * @return GroupAndSortingDefinitionCollectionInterface|GroupAndSortingDefinitionInterface[] + * @return GroupAndSortingDefinitionCollectionInterface */ protected function getGroupAndSortingDefinition() { + $definition = $this->getEnvironment()->getDataDefinition(); + assert($definition instanceof ContainerInterface); + /** @var Contao2BackendViewDefinitionInterface $view */ - $view = $this->getEnvironment() - ->getDataDefinition() - ->getDefinition(Contao2BackendViewDefinitionInterface::NAME); + $view = $definition->getDefinition(Contao2BackendViewDefinitionInterface::NAME); return $view ->getListingConfig() @@ -62,7 +64,7 @@ protected function getGroupAndSortingDefinition() } /** - * Search a definition by it's name. + * Search a definition by its name. * * @param string $name The name. * @@ -82,20 +84,23 @@ protected function searchDefinitionByName($name) /** * Retrieve the persistent value from the input provider. * - * @return array + * @return string */ protected function getPersistent() { + $definition = $this->getEnvironment()->getDataDefinition(); + assert($definition instanceof ContainerInterface); + $values = []; if ($this->getSessionStorage()->has('sorting')) { $values = $this->getSessionStorage()->get('sorting'); } - if (\array_key_exists($this->getEnvironment()->getDataDefinition()->getName(), $values)) { - return $values[$this->getEnvironment()->getDataDefinition()->getName()]; + if (\array_key_exists($definition->getName(), $values)) { + return $values[$definition->getName()]; } - return []; + return ''; } /** @@ -107,18 +112,18 @@ protected function getPersistent() */ protected function setPersistent($propertyName) { - $values = []; - $definitionName = $this->getEnvironment()->getDataDefinition()->getName(); + $definition = $this->getEnvironment()->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $definitionName = $definition->getName(); + + $values = []; if ($this->getSessionStorage()->has('sorting')) { $values = $this->getSessionStorage()->get('sorting'); } if ($propertyName) { - if (isset($values[$definitionName]) && !\is_array($values[$definitionName])) { - $values[$definitionName] = []; - } - $values[$definitionName] = $propertyName; } else { unset($values[$definitionName]); @@ -130,18 +135,14 @@ protected function setPersistent($propertyName) /** * {@inheritDoc} */ - public function initialize(ConfigInterface $config, PanelElementInterface $panelElement = null) + public function initialize(ConfigInterface $config, PanelElementInterface $element = null) { - $this->defineSortOption($panelElement); + $this->defineSortOption($element); $current = $config->getSorting(); - if (!\is_array($current)) { - $current = []; - } - - if ($this->getSelectedDefinition()) { - foreach ($this->getSelectedDefinition() as $information) { + if (null !== $selected = $this->getSelectedDefinition()) { + foreach ($selected as $information) { $current[$information->getProperty()] = $information->getSortingMode(); } } @@ -162,9 +163,11 @@ private function defineSortOption(?PanelElementInterface $panelElement): void } $input = $this->getInputProvider(); - $value = null; + assert($input instanceof InputProviderInterface); - if ('1' !== $this->getEnvironment()->getInputProvider()->getValue('filter_reset')) { + $value = ''; + + if ('1' !== $input->getValue('filter_reset')) { if ($input->hasValue('tl_sort') && $this->getPanel()->getContainer()->updateValues()) { $value = $input->getValue('tl_sort'); @@ -181,7 +184,7 @@ private function defineSortOption(?PanelElementInterface $panelElement): void $this->setSelected($persistent); } else { - $this->setPersistent(null); + $this->setPersistent(''); } } @@ -190,11 +193,14 @@ private function defineSortOption(?PanelElementInterface $panelElement): void */ public function render(ViewTemplateInterface $viewTemplate) { + $definition = $this->getEnvironment()->getDataDefinition(); + assert($definition instanceof ContainerInterface); + $options = []; foreach ($this->getGroupAndSortingDefinition() as $information) { /** @var GroupAndSortingDefinitionInterface $information */ $name = $information->getName(); - $properties = $this->getEnvironment()->getDataDefinition()->getPropertiesDefinition(); + $properties = $definition->getPropertiesDefinition(); if ($properties->hasProperty($name)) { $name = $properties->getProperty($name)->getLabel(); } diff --git a/src/Panel/DefaultSubmitElement.php b/src/Panel/DefaultSubmitElement.php index 7e6ffd5b..fdd16990 100644 --- a/src/Panel/DefaultSubmitElement.php +++ b/src/Panel/DefaultSubmitElement.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -32,7 +33,7 @@ class DefaultSubmitElement extends AbstractElement implements SubmitElementInter /** * {@inheritdoc} */ - public function initialize(ConfigInterface $config, PanelElementInterface $panelElement = null) + public function initialize(ConfigInterface $config, PanelElementInterface $element = null) { } @@ -41,5 +42,6 @@ public function initialize(ConfigInterface $config, PanelElementInterface $panel */ public function render(ViewTemplateInterface $viewTemplate) { + return $this; } } diff --git a/src/Panel/PanelContainerInterface.php b/src/Panel/PanelContainerInterface.php index d59602a5..b38309fd 100644 --- a/src/Panel/PanelContainerInterface.php +++ b/src/Panel/PanelContainerInterface.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -28,6 +29,8 @@ * This interface describes a panel container. * * A Panel container contains panels which contain panel elements. + * + * @extends \IteratorAggregate */ interface PanelContainerInterface extends \IteratorAggregate, \Countable { @@ -50,31 +53,31 @@ public function setEnvironment(EnvironmentInterface $objEnvironment); /** * Add a panel to the container. * - * @param string $strKey Name of the panel. - * @param PanelInterface $objPanel The panel to add. + * @param string $panelName Name of the panel. + * @param PanelInterface $panel The panel to add. * * @return PanelContainerInterface */ - public function addPanel($strKey, $objPanel); + public function addPanel($panelName, $panel); /** * Retrieve a panel from the container. * - * @param string $strKey The name of the panel. + * @param string $panelName The name of the panel. * * @return PanelInterface */ - public function getPanel($strKey); + public function getPanel($panelName); /** * Initialize all panels and apply all restrictions to the given Config. * - * @param ConfigInterface $objConfig The data config to be populated with the element values. - * @param PanelElementInterface $objElement The element currently being initialized. + * @param ConfigInterface $config The data config to be populated with the element values. + * @param PanelElementInterface|null $element The element currently being initialized. * * @return PanelContainerInterface */ - public function initialize(ConfigInterface $objConfig, PanelElementInterface $objElement = null); + public function initialize(ConfigInterface $config, PanelElementInterface $element = null); /** * Determinator if the panels should be updated from the InputProvider or not. diff --git a/src/Panel/PanelElementInterface.php b/src/Panel/PanelElementInterface.php index 3cef7230..825d28bd 100644 --- a/src/Panel/PanelElementInterface.php +++ b/src/Panel/PanelElementInterface.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -39,28 +40,28 @@ public function getPanel(); /** * Return the parenting panel. * - * @param PanelInterface $objPanel The panel to use as parent. + * @param PanelInterface $panelElement The panel to use as parent. * * @return PanelElementInterface */ - public function setPanel(PanelInterface $objPanel); + public function setPanel(PanelInterface $panelElement); /** * Initialize the passed configuration with the values of the element. * - * @param ConfigInterface $objConfig The config to which the initialization shall be applied to. - * @param PanelElementInterface $objElement The element to be initialized (if any). + * @param ConfigInterface $config The config to which the initialization shall be applied to. + * @param PanelElementInterface|null $element The element to be initialized (if any). * * @return void */ - public function initialize(ConfigInterface $objConfig, PanelElementInterface $objElement = null); + public function initialize(ConfigInterface $config, PanelElementInterface $element = null); /** * Render the element using the given Template. * - * @param ViewTemplateInterface $objTemplate The Template to use. + * @param ViewTemplateInterface $viewTemplate The Template to use. * - * @return PanelElementInterface + * @return self */ - public function render(ViewTemplateInterface $objTemplate); + public function render(ViewTemplateInterface $viewTemplate); } diff --git a/src/Panel/PanelInterface.php b/src/Panel/PanelInterface.php index 4d853105..ee825928 100644 --- a/src/Panel/PanelInterface.php +++ b/src/Panel/PanelInterface.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -27,6 +28,8 @@ * This interface describes a panel. * * A panel is a row of a panel container. + * + * @extends \IteratorAggregate */ interface PanelInterface extends \IteratorAggregate, \Countable { @@ -40,38 +43,38 @@ public function getContainer(); /** * Set the parenting container. * - * @param PanelContainerInterface $objContainer The Container to be used as parent. + * @param PanelContainerInterface $container The Container to be used as parent. * * @return PanelInterface */ - public function setContainer(PanelContainerInterface $objContainer); + public function setContainer(PanelContainerInterface $container); /** * Add an element to the panel. * - * @param string $strKey Name of the panel. - * @param PanelElementInterface $objElement The element instance to add. + * @param string $panelName Name of the panel. + * @param PanelElementInterface $element The element instance to add. * * @return mixed */ - public function addElement($strKey, $objElement); + public function addElement($panelName, $element); /** * Retrieve an element with the given name. * - * @param string $strKey The name of the element. + * @param string $elementName The name of the element. * - * @return PanelElementInterface + * @return PanelElementInterface|null */ - public function getElement($strKey); + public function getElement($elementName); /** * Initialize the passed config via all contained elements. * - * @param ConfigInterface $objConfig The config to which the initialization shall be applied to. - * @param PanelElementInterface $objElement The element to be initialized (if any). + * @param ConfigInterface $config The config to which the initialization shall be applied to. + * @param PanelElementInterface|null $element The element to be initialized (if any). * * @return void */ - public function initialize(ConfigInterface $objConfig, PanelElementInterface $objElement = null); + public function initialize(ConfigInterface $config, PanelElementInterface $element = null); } diff --git a/src/Panel/SortElementInterface.php b/src/Panel/SortElementInterface.php index bf052a92..21cbe383 100644 --- a/src/Panel/SortElementInterface.php +++ b/src/Panel/SortElementInterface.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -22,7 +23,6 @@ namespace ContaoCommunityAlliance\DcGeneral\Panel; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\GroupAndSortingDefinitionInterface; -use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\GroupAndSortingInformationInterface; /** * This interface describes a sort panel element. @@ -34,21 +34,21 @@ interface SortElementInterface extends PanelElementInterface * * @param string $name The name of the definition to mark as selected. * - * @return SearchElementInterface + * @return self */ public function setSelected($name); /** * Return the name of the currently selected definition. * - * @return string + * @return string|null */ public function getSelected(); /** * Return the currently selected definition. * - * @return GroupAndSortingDefinitionInterface|GroupAndSortingInformationInterface[] + * @return GroupAndSortingDefinitionInterface|null */ public function getSelectedDefinition(); } diff --git a/src/Resources/config/contao/picker_provider.yml b/src/Resources/config/contao/picker_provider.yml index 5a7765fe..03964f72 100644 --- a/src/Resources/config/contao/picker_provider.yml +++ b/src/Resources/config/contao/picker_provider.yml @@ -5,10 +5,12 @@ services: arguments: - "@knp_menu.factory" - "@router" + - "@?translator" + - "@security.helper" calls: - [setTokenStorage, ["@security.token_storage"]] tags: - - name: contao.picker_provider + - name: contao.picker_provider cca.picker.tree_picker_provider: class: ContaoCommunityAlliance\DcGeneral\Contao\Picker\TreePickerProvider @@ -20,4 +22,4 @@ services: calls: - [setTokenStorage, ["@security.token_storage"]] tags: - - name: contao.picker_provider + - name: contao.picker_provider diff --git a/src/Resources/contao/templates/dcbe_general_edit.html5 b/src/Resources/contao/templates/dcbe_general_edit.html5 index 12f120c4..5665d940 100644 --- a/src/Resources/contao/templates/dcbe_general_edit.html5 +++ b/src/Resources/contao/templates/dcbe_general_edit.html5 @@ -74,7 +74,7 @@ $this->insert( -fieldsets as $arrFieldset): if($arrFieldset['legend']): ?> +fieldsets as $arrFieldset): if($arrFieldset['legend'] ?? null): ?>
diff --git a/src/View/ActionHandler/AbstractEnvironmentAwareHandler.php b/src/View/ActionHandler/AbstractEnvironmentAwareHandler.php index 8ac25cf8..c33b9801 100644 --- a/src/View/ActionHandler/AbstractEnvironmentAwareHandler.php +++ b/src/View/ActionHandler/AbstractEnvironmentAwareHandler.php @@ -26,11 +26,13 @@ use ContaoCommunityAlliance\DcGeneral\Event\ActionEvent; /** - * The AbstractEnvironmentAwareHandler is designed for action handlers which can also be used in a non event context. + * The AbstractEnvironmentAwareHandler is designed for action handlers which can also be used in a nonevent context. * * It provides a setEnvironment method which has to be used to initialize the environment instead. * * @deprecated This class is deprecated as it is an event listener with a changing state and will get removed. + * + * @psalm-suppress DeprecatedClass */ abstract class AbstractEnvironmentAwareHandler extends AbstractHandler implements EnvironmentAwareInterface { diff --git a/src/View/ActionHandler/AbstractPropertyOverrideEditAllHandler.php b/src/View/ActionHandler/AbstractPropertyOverrideEditAllHandler.php index 7b2a6051..5f2a5978 100644 --- a/src/View/ActionHandler/AbstractPropertyOverrideEditAllHandler.php +++ b/src/View/ActionHandler/AbstractPropertyOverrideEditAllHandler.php @@ -14,7 +14,7 @@ * @author Sven Baumann * @author Richard Henkenjohann * @author Ingolf Steinhardt - * @copyright 2013-2023s Contao Community Alliance. + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0 * @filesource */ @@ -31,6 +31,8 @@ use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\ContaoBackendViewTemplate; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\GetBreadcrumbEvent; use ContaoCommunityAlliance\DcGeneral\Data\CollectionInterface; +use ContaoCommunityAlliance\DcGeneral\Data\DataProviderInterface; +use ContaoCommunityAlliance\DcGeneral\Data\EditInformationInterface; use ContaoCommunityAlliance\DcGeneral\Data\ModelId; use ContaoCommunityAlliance\DcGeneral\Data\ModelIdInterface; use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; @@ -43,7 +45,9 @@ use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralRuntimeException; use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; use ContaoCommunityAlliance\DcGeneral\SessionStorageInterface; +use ContaoCommunityAlliance\Translator\TranslatorInterface; use LogicException; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; /** * This class is the abstract base for override/edit all "overrideAll/editAll" commands. @@ -53,6 +57,8 @@ */ abstract class AbstractPropertyOverrideEditAllHandler extends AbstractPropertyVisibilityHandler { + use CallActionTrait; + /** * Handle submit triggered button. * @@ -69,6 +75,7 @@ protected function handleSubmit(Action $action, EnvironmentInterface $environmen $inputProvider = $this->getInputProvider($environment); $sessionStorage = $this->getSessionStorage($environment); $eventDispatcher = $environment->getEventDispatcher(); + assert($eventDispatcher instanceof EventDispatcherInterface); if ( ('auto' === $inputProvider->getValue('SUBMIT_TYPE')) @@ -110,6 +117,7 @@ protected function editCollection( ) { while ($collection->count() > 0) { $model = $collection->shift(); + assert($model instanceof ModelInterface); $persistPropertyValueBag = $this->cloneCleanPropertyValueBag($action, $propertyValueBag, $model, $environment); @@ -131,6 +139,7 @@ protected function editCollection( protected function updateErrorInformation( \ArrayObject $renderInformation ) { + /** @var array>>|null $modelError */ $modelError = $renderInformation->offsetExists('modelError') ? $renderInformation->offsetGet('modelError') : null; @@ -232,8 +241,10 @@ private function markPropertyInvalidErrorsByModel( EnvironmentInterface $environment ) { $editInformation = System::getContainer()->get('cca.dc-general.edit-information'); + assert($editInformation instanceof EditInformationInterface); + $errorInformation = $editInformation->getModelError($model); - if (null === $errorInformation) { + if (!$errorInformation) { return; } @@ -301,7 +312,9 @@ private function updatePropertyValueBag( PropertyValueBagInterface $updateBag, EnvironmentInterface $environment ) { - $dataProvider = $environment->getDataProvider(); + $dataProvider = $environment->getDataProvider(); + assert($dataProvider instanceof DataProviderInterface); + $sessionProperties = $this->getPropertiesFromSession($action, $environment); foreach (\array_keys($sessionProperties) as $sessionPropertyName) { @@ -311,6 +324,8 @@ private function updatePropertyValueBag( if (!$updateBag->isPropertyValueInvalid($sessionPropertyName)) { $editModel = $dataProvider->fetch($dataProvider->getEmptyConfig()->setId($model->getId())); + assert($editModel instanceof ModelInterface); + $updateBag->setPropertyValue( $sessionPropertyName, $editModel->getProperty($sessionPropertyName) @@ -348,8 +363,11 @@ private function handleEditHandler( PropertyValueBagInterface $propertyValueBag, EnvironmentInterface $environment ) { - $inputProvider = $this->getInputProvider($environment); + $inputProvider = $this->getInputProvider($environment); + assert($inputProvider instanceof InputProviderInterface); + $editInformation = System::getContainer()->get('cca.dc-general.edit-information'); + assert($editInformation instanceof EditInformationInterface); $inputValues = $this->handleInputValues($action, $model, $environment); @@ -370,7 +388,7 @@ private function handleEditHandler( $this->restoreInputValues($action, $model, $propertyValueBag, $inputValues, $environment); $errorInformation = $editInformation->getModelError($model); - if (null !== $errorInformation) { + if ($errorInformation) { foreach (\array_keys($errorInformation) as $errorPropertyName) { if (false === $propertyValueBag->hasPropertyValue($errorPropertyName)) { continue; @@ -407,9 +425,11 @@ private function handleInputValues(Action $action, ModelInterface $model, Enviro } $inputProvider = $this->getInputProvider($environment); + assert($inputProvider instanceof InputProviderInterface); $inputValues = []; foreach (\array_keys($_POST) as $valueName) { + $valueName = (string) $valueName; $inputValues[$valueName] = $inputProvider->getValue($valueName, true); $inputProvider->unsetValue($valueName); @@ -479,11 +499,12 @@ protected function restoreInputValues( * @param Action $action The action. * @param EnvironmentInterface $environment The environment. * - * @return array + * @return string */ protected function getEditButtons(Action $action, EnvironmentInterface $environment) { $translator = $environment->getTranslator(); + assert($translator instanceof TranslatorInterface); $mode = $this->getMode($action); @@ -548,7 +569,9 @@ protected function getPropertyValueBagFromModel( EnvironmentInterface $environment ) { $propertiesDefinition = $this->getDataDefinition($environment)->getPropertiesDefinition(); - $editInformation = System::getContainer()->get('cca.dc-general.edit-information'); + + $editInformation = System::getContainer()->get('cca.dc-general.edit-information'); + assert($editInformation instanceof EditInformationInterface); $propertyValueBag = new PropertyValueBag(); @@ -594,6 +617,7 @@ protected function getCollectionFromSession(Action $action, EnvironmentInterface $sessionStorage = $this->getSessionStorage($environment); $dataDefinition = $this->getDataDefinition($environment); $dataProvider = $environment->getDataProvider($dataDefinition->getName()); + assert($dataProvider instanceof DataProviderInterface); $addEditProperties = $inputProvider->hasValue('FORM_INPUTS') @@ -608,8 +632,9 @@ protected function getCollectionFromSession(Action $action, EnvironmentInterface $modelIds[] = ModelId::fromSerialized($modelId)->getId(); if ($addEditProperties) { - $modelEditProperties = $inputProvider->getValue(\str_replace('::', '____', $modelId) . '_', true); - $inputProvider->unsetValue(\str_replace('::', '____', $modelId) . '_'); + $transformed = \str_replace('::', '____', (string) $modelId) . '_'; + $modelEditProperties = $inputProvider->getValue($transformed, true); + $inputProvider->unsetValue($transformed); $editProperties[$modelId] = $modelEditProperties; } @@ -621,6 +646,7 @@ protected function getCollectionFromSession(Action $action, EnvironmentInterface [['operation' => 'IN', 'property' => $idProperty, 'values' => $modelIds]] ) ); + assert($collection instanceof CollectionInterface); $session['editProperties'] = $editProperties; @@ -644,7 +670,7 @@ protected function getEditPropertiesByModelId( EnvironmentInterface $environment ) { $session = $this->getSession($action, $environment); -//dd($session['editProperties']); + return $session['editProperties'][$modelId->getSerialized()] ?? []; } @@ -660,8 +686,11 @@ protected function getEditPropertiesByModelId( */ protected function renderBreadcrumb(EnvironmentInterface $environment) { + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + $event = new GetBreadcrumbEvent($environment); - $environment->getEventDispatcher()->dispatch($event, $event::NAME); + $dispatcher->dispatch($event, $event::NAME); $elements = $event->getElements(); if (empty($elements)) { return null; @@ -692,15 +721,20 @@ protected function revertValuesByErrors( EnvironmentInterface $environment ) { $editInformation = System::getContainer()->get('cca.dc-general.edit-information'); + assert($editInformation instanceof EditInformationInterface); + if (!$editInformation->hasAnyModelError()) { return; } $dataProvider = $environment->getDataProvider(); + assert($dataProvider instanceof DataProviderInterface); + $properties = $this->getPropertiesFromSession($action, $environment); while ($collection->count() > 0) { $model = $collection->shift(); + assert($model instanceof ModelInterface); $modelErrors = $editInformation->getModelError($model); if (!$modelErrors && ('edit' === $this->getMode($action))) { @@ -708,6 +742,7 @@ protected function revertValuesByErrors( } $revertModel = $dataProvider->fetch($dataProvider->getEmptyConfig()->setId($model->getId())); + assert($revertModel instanceof ModelInterface); $originalModel = clone $revertModel; $revertModel->setId($revertModel->getId()); @@ -741,7 +776,11 @@ private function handlePostPersist( EnvironmentInterface $environment ) { $event = new PostPersistModelEvent($environment, $model, $originalModel); - $environment->getEventDispatcher()->dispatch($event, $event::NAME); + + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $dispatcher->dispatch($event, $event::NAME); } /** @@ -765,6 +804,7 @@ protected function getSession(Action $action, EnvironmentInterface $environment) { $dataDefinition = $this->getDataDefinition($environment); $sessionStorage = $environment->getSessionStorage(); + assert($sessionStorage instanceof SessionStorageInterface); $session = $sessionStorage->get($dataDefinition->getName() . '.' . $this->getMode($action)); diff --git a/src/View/ActionHandler/AbstractPropertyVisibilityHandler.php b/src/View/ActionHandler/AbstractPropertyVisibilityHandler.php index 66713b4b..71fdd757 100644 --- a/src/View/ActionHandler/AbstractPropertyVisibilityHandler.php +++ b/src/View/ActionHandler/AbstractPropertyVisibilityHandler.php @@ -30,6 +30,7 @@ use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\PalettesDefinitionInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\Properties\PropertyInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Condition\Property\PropertyConditionInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Condition\Property\PropertyTrueCondition; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\PaletteInterface; use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; @@ -389,7 +390,10 @@ private function invisiblePaletteProperty( continue; } - if (false === $searchProperty->getVisibleCondition()->match($intersectModel)) { + $visibleCondition = $searchProperty->getVisibleCondition(); + assert($visibleCondition instanceof PropertyConditionInterface); + + if (false === $visibleCondition->match($intersectModel)) { continue; } diff --git a/src/View/ViewTemplateInterface.php b/src/View/ViewTemplateInterface.php index 9941d11c..7f691598 100644 --- a/src/View/ViewTemplateInterface.php +++ b/src/View/ViewTemplateInterface.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -29,11 +30,11 @@ interface ViewTemplateInterface /** * Set the template data from an array. * - * @param array $data The data array. + * @param array $arrData The data array. * - * @return ViewTemplateInterface + * @return self */ - public function setData($data); + public function setData($arrData); /** * Return the template data as array. @@ -48,7 +49,7 @@ public function getData(); * @param string $name Name of the value. * @param mixed $value The value to add to the template. * - * @return ViewTemplateInterface + * @return self */ public function set($name, $value); diff --git a/tests/Data/DefaultDataProviderTest.php b/tests/Data/DefaultDataProviderTest.php index 92dc82e2..80e6e966 100644 --- a/tests/Data/DefaultDataProviderTest.php +++ b/tests/Data/DefaultDataProviderTest.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,8 @@ * @package contao-community-alliance/dc-general * @author Christian Schiffler * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -52,7 +53,7 @@ private function mockDatabase() return $this ->getMockBuilder(Database::class) ->disableOriginalConstructor() - ->setMethods(['__destruct', 'listFields']) + ->onlyMethods(['__destruct', 'listFields']) ->getMockForAbstractClass(); } @@ -61,7 +62,7 @@ private function mockConnection() return $this ->getMockBuilder(Connection::class) ->disableOriginalConstructor() - ->setMethods(['getSchemaManager']) + ->onlyMethods(['createSchemaManager']) ->getMock(); } @@ -75,19 +76,19 @@ private function mockDefaultProvider() $schemaTable = $this ->getMockBuilder(Table::class) ->disableOriginalConstructor() - ->setMethods(['hasColumn']) + ->onlyMethods(['hasColumn']) ->getMock(); $schemaTable->method('hasColumn')->willReturn(false); $schemaManager = $this ->getMockBuilder(AbstractSchemaManager::class) ->disableOriginalConstructor() - ->setMethods(['listTableDetails']) + ->onlyMethods(['introspectTable']) ->getMockForAbstractClass(); - $schemaManager->method('listTableDetails')->willReturn($schemaTable); + $schemaManager->method('introspectTable')->willReturn($schemaTable); $connection = $this->mockConnection(); - $connection->method('getSchemaManager')->willReturn($schemaManager); + $connection->method('createSchemaManager')->willReturn($schemaManager); //$database->method('listFields')->willReturn([]); $dataProvider = new DefaultDataProvider(); @@ -122,19 +123,19 @@ public function testSetBaseConfigDeprecatedDatabase() $schemaTable = $this ->getMockBuilder(Table::class) ->disableOriginalConstructor() - ->setMethods(['hasColumn']) + ->onlyMethods(['hasColumn']) ->getMock(); $schemaTable->method('hasColumn')->willReturn(false); $schemaManager = $this ->getMockBuilder(AbstractSchemaManager::class) ->disableOriginalConstructor() - ->setMethods(['listTableDetails']) + ->onlyMethods(['introspectTable']) ->getMockForAbstractClass(); - $schemaManager->method('listTableDetails')->willReturn($schemaTable); + $schemaManager->method('introspectTable')->willReturn($schemaTable); $connection = $this->mockConnection(); - $connection->method('getSchemaManager')->willReturn($schemaManager); + $connection->method('createSchemaManager')->willReturn($schemaManager); $reflection = new \ReflectionProperty(Database::class, 'resConnection'); $reflection->setAccessible(true); @@ -160,7 +161,7 @@ public function testSetBaseConfigDeprecatedDatabase() $reflection->setAccessible(true); self::assertSame('id', $reflection->getValue($dataProvider)); - self::assertFalse($dataProvider->getTimeStampProperty()); + self::assertNull($dataProvider->getTimeStampProperty()); self::assertNull($dataProvider->getIdGenerator()); } @@ -186,24 +187,24 @@ public function testSetBaseConfigForGetDefaultConnection() $schemaTable = $this ->getMockBuilder(Table::class) ->disableOriginalConstructor() - ->setMethods(['hasColumn']) + ->onlyMethods(['hasColumn']) ->getMock(); $schemaTable->method('hasColumn')->willReturn(false); $schemaManager = $this ->getMockBuilder(AbstractSchemaManager::class) ->disableOriginalConstructor() - ->setMethods(['listTableDetails']) + ->onlyMethods(['introspectTable']) ->getMockForAbstractClass(); - $schemaManager->method('listTableDetails')->willReturn($schemaTable); + $schemaManager->method('introspectTable')->willReturn($schemaTable); $connection = $this->mockConnection(); - $connection->method('getSchemaManager')->willReturn($schemaManager); + $connection->method('createSchemaManager')->willReturn($schemaManager); $dataProvider = $this ->getMockBuilder(DefaultDataProvider::class) ->disableOriginalConstructor() - ->setMethods(['getDefaultConnection']) + ->onlyMethods(['getDefaultConnection']) ->getMock(); $dataProvider->method('getDefaultConnection')->willReturn($connection); From 9145cdd74f9622872bd7435109a0a773a7b4eb74 Mon Sep 17 00:00:00 2001 From: Ingolf Steinhardt Date: Mon, 11 Sep 2023 15:38:55 +0200 Subject: [PATCH 53/53] Any fixes --- .composer-require-checker.json | 8 +- .github/workflows/diagnostics.yml | 2 +- .phpcq.yaml.dist | 1 + composer.json | 2 +- src/Clipboard/ClipboardInterface.php | 2 +- src/Clipboard/Filter.php | 70 +-- .../Callback/ModelLabelCallbackListener.php | 7 +- ...rtyInputFieldGetWizardCallbackListener.php | 11 +- ...rtyInputFieldGetXLabelCallbackListener.php | 11 +- src/Contao/Compatibility/DcCompat.php | 7 +- .../Legacy/LegacyDcaDataDefinitionBuilder.php | 6 +- .../Dca/Populator/BackendViewPopulator.php | 4 + .../Dca/Populator/HardCodedPopulator.php | 13 +- src/Contao/Factory/SessionStorageFactory.php | 7 +- src/Contao/Picker/PagePickerProvider.php | 37 +- src/Contao/SessionStorage.php | 40 +- .../DynamicParentTableSubscriber.php | 14 +- .../Subscriber/FallbackResetSubscriber.php | 53 +- .../Subscriber/FormatModelLabelSubscriber.php | 31 +- .../AbstractListShowAllHandler.php | 270 ++++++--- .../ActionHandler/CopyHandler.php | 112 +++- .../ActionHandler/CreateHandler.php | 38 +- .../ActionHandler/DeleteHandler.php | 79 ++- .../ActionHandler/EditHandler.php | 48 +- .../ActionHandler/ListViewShowAllHandler.php | 20 +- .../MultipleHandler/EditAllHandler.php | 74 ++- .../MultipleHandler/OverrideAllHandler.php | 94 +++- .../MultipleHandler/PasteAllHandler.php | 108 ++-- .../MultipleHandler/SelectModelAllHandler.php | 10 +- .../SelectPropertyAllHandler.php | 110 ++-- .../ParentedListViewShowAllHandler.php | 195 +++++-- .../ActionHandler/PasteHandler.php | 44 +- .../ActionHandler/SelectHandler.php | 311 ++++++----- .../ActionHandler/ShowHandler.php | 145 ++++- .../ActionHandler/ToggleHandler.php | 59 +- .../View/Contao2BackendView/BaseView.php | 212 +++++-- .../Contao2BackendView/ButtonRenderer.php | 163 ++++-- .../ContaoBackendViewTemplate.php | 70 ++- .../ContaoWidgetManager.php | 127 +++-- .../Controller/ClipboardController.php | 97 +++- .../View/Contao2BackendView/EditMask.php | 325 +++++++---- .../Event/BaseButtonEvent.php | 21 +- .../Event/BaseGetButtonsEvent.php | 9 +- .../Event/BuildWidgetEvent.php | 11 +- .../DecodePropertyValueForWidgetEvent.php | 11 +- .../EncodePropertyValueFromWidgetEvent.php | 7 +- .../Event/GetBreadcrumbEvent.php | 7 +- .../Event/GetGlobalButtonEvent.php | 11 +- .../Event/GetGroupHeaderEvent.php | 6 +- .../Event/GetOperationButtonEvent.php | 4 +- .../Event/GetParentHeaderEvent.php | 7 +- .../Event/GetPasteButtonEvent.php | 7 +- .../Event/GetPasteRootButtonEvent.php | 9 +- .../Event/ModelToLabelEvent.php | 8 +- .../Event/ParentViewChildRecordEvent.php | 2 +- .../PrepareMultipleModelsActionEvent.php | 18 +- .../EventListener/BackButtonListener.php | 19 +- .../ColorPickerWizardListener.php | 16 +- .../CreateModelButtonListener.php | 36 +- .../CreateSubHeadlineListener.php | 14 +- .../SelectModeButtonsListener.php | 18 +- .../View/Contao2BackendView/FileSelect.php | 141 +++-- .../Filter/LanguageFilter.php | 70 ++- .../GlobalButtonRenderer.php | 45 +- .../View/Contao2BackendView/PanelBuilder.php | 22 +- .../View/Contao2BackendView/PanelRenderer.php | 31 +- .../View/Contao2BackendView/ParentView.php | 11 +- .../Subscriber/CheckPermission.php | 19 +- .../Subscriber/GetGroupHeaderSubscriber.php | 46 +- .../Subscriber/MultipleHandlerSubscriber.php | 95 +++- .../Subscriber/RichTextFileUuidSubscriber.php | 18 +- .../Subscriber/WidgetBuilder.php | 128 +++-- .../View/Contao2BackendView/TreePicker.php | 515 +++++++++++++----- .../View/Contao2BackendView/TreeSelect.php | 48 +- .../View/Contao2BackendView/TreeView.php | 229 ++++++-- .../View/Contao2BackendView/ViewHelpers.php | 54 +- .../Widget/AbstractWidget.php | 14 +- .../Contao2BackendView/Widget/FileTree.php | 156 ++++-- .../Widget/FileTreeOrder.php | 13 +- .../Contao2BackendView/Widget/PageTree.php | 30 +- .../Widget/PageTreeOrder.php | 2 + .../Widget/TreePickerOrder.php | 6 +- src/ContaoManager/Plugin.php | 7 +- src/Controller/Ajax.php | 72 ++- src/Controller/Ajax3X.php | 166 ++++-- src/Controller/BackendTreeController.php | 223 +++++--- src/Controller/ControllerInterface.php | 38 +- src/Controller/DefaultController.php | 380 +++++++++---- src/Controller/ModelCollector.php | 160 ++++-- src/Controller/SortingManager.php | 107 ++-- src/Controller/TreeCollector.php | 109 ++-- src/Controller/TreeNodeStates.php | 17 +- src/DC/General.php | 48 +- src/Data/AbstractModel.php | 15 +- src/Data/ConfigInterface.php | 6 +- src/Data/DataProviderInterface.php | 4 +- src/Data/DefaultCollection.php | 21 +- src/Data/DefaultConfig.php | 87 +-- src/Data/DefaultDataProvider.php | 93 ++-- src/Data/DefaultDataProviderDBalUtils.php | 130 +++-- src/Data/DefaultDataProviderSqlUtils.php | 24 +- src/Data/DefaultEditInformation.php | 27 +- src/Data/DefaultFilterOptionCollection.php | 4 +- src/Data/DefaultLanguageInformation.php | 6 +- .../DefaultLanguageInformationCollection.php | 2 +- src/Data/DefaultModel.php | 45 +- src/Data/EditInformationInterface.php | 4 +- ...LanguageInformationCollectionInterface.php | 7 +- src/Data/LanguageInformationInterface.php | 2 +- src/Data/ModelId.php | 1 + src/Data/ModelInterface.php | 23 +- src/Data/MultiLanguageDataProvider.php | 2 + .../MultiLanguageDataProviderInterface.php | 9 +- src/Data/NoOpDataProvider.php | 7 +- src/Data/PropertyValueBag.php | 22 +- src/Data/PropertyValueBagInterface.php | 13 +- src/Data/TableRowsAsRecordsDataProvider.php | 44 +- src/DataDefinition/AbstractConditionChain.php | 2 +- ...stractEventDrivenDataDefinitionBuilder.php | 19 +- .../DataProviderInformation.php | 4 +- src/DataDefinition/DefaultContainer.php | 39 +- .../Definition/BasicDefinitionInterface.php | 2 +- .../DataProviderDefinitionInterface.php | 8 +- .../Definition/DefaultBasicDefinition.php | 6 +- .../DefaultDataProviderDefinition.php | 22 +- .../DefaultModelRelationshipDefinition.php | 9 +- .../DefaultPropertiesDefinition.php | 2 +- .../Definition/Properties/DefaultProperty.php | 48 +- .../PropertiesDefinitionInterface.php | 7 +- .../Definition/View/Command.php | 19 +- .../Definition/View/CommandCollection.php | 6 +- .../View/CommandCollectionInterface.php | 2 +- .../Definition/View/CommandInterface.php | 4 +- ...ultGroupAndSortingDefinitionCollection.php | 2 + .../DefaultGroupAndSortingInformation.php | 4 +- .../Definition/View/DefaultListingConfig.php | 8 +- .../View/DefaultModelFormatterConfig.php | 11 +- .../Definition/View/DefaultPanelRow.php | 21 +- .../View/DefaultPanelRowCollection.php | 2 +- .../View/ListingConfigInterface.php | 4 +- .../Panel/DefaultFilterElementInformation.php | 9 +- .../Panel/DefaultSearchElementInformation.php | 9 +- .../View/PanelRowCollectionInterface.php | 9 +- .../Definition/View/PanelRowInterface.php | 9 +- .../Definition/View/ToggleCommand.php | 10 +- .../View/TranslatedToggleCommand.php | 7 +- .../ModelRelationship/FilterBuilder.php | 11 +- .../FilterBuilder/AndFilterBuilder.php | 9 +- .../BaseComparingFilterBuilder.php | 18 +- .../FilterBuilder/BaseFilterBuilder.php | 51 +- .../FilterBuilderWithChildren.php | 60 +- .../FilterBuilder/OrFilterBuilder.php | 7 +- .../PropertyValueInFilterBuilder.php | 9 +- .../ParentChildCondition.php | 64 +-- .../ParentChildConditionInterface.php | 17 +- .../ModelRelationship/RootCondition.php | 15 +- .../RootConditionInterface.php | 8 +- .../Builder/Event/AddConditionEvent.php | 26 +- .../Palette/Builder/Event/BuilderEvent.php | 5 +- .../Builder/Event/CreateConditionEvent.php | 19 +- .../CreateDefaultPaletteConditionEvent.php | 21 +- .../CreatePaletteConditionChainEvent.php | 26 +- .../CreatePropertyConditionChainEvent.php | 27 +- ...tDefaultPaletteConditionClassNameEvent.php | 18 +- .../Builder/Event/SetLegendClassNameEvent.php | 13 +- .../Event/SetPaletteClassNameEvent.php | 13 +- .../SetPaletteCollectionClassNameEvent.php | 13 +- ...SetPaletteConditionChainClassNameEvent.php | 21 +- ...tePropertyValueConditionClassNameEvent.php | 18 +- .../Event/SetPropertyClassNameEvent.php | 18 +- ...etPropertyConditionChainClassNameEvent.php | 22 +- ...etPropertyValueConditionClassNameEvent.php | 18 +- .../Palette/Builder/PaletteBuilder.php | 293 +++++----- .../Palette/AbstractBoolPaletteCondition.php | 13 +- .../Palette/PaletteConditionChain.php | 10 +- .../Palette/PaletteConditionInterface.php | 11 +- .../Palette/PropertyValueCondition.php | 13 +- .../Condition/Property/BooleanCondition.php | 9 +- .../Property/DumpingPropertyCondition.php | 9 +- .../PropertyBooleanConditionTrait.php | 111 ++++ .../Property/PropertyConditionInterface.php | 21 +- .../Property/PropertyEditableCondition.php | 13 +- .../Property/PropertyFalseCondition.php | 88 +-- .../Property/PropertyTrueCondition.php | 86 +-- .../Property/PropertyValueCondition.php | 16 +- .../Property/PropertyVisibleCondition.php | 8 +- src/DataDefinition/Palette/Legend.php | 25 +- src/DataDefinition/Palette/Palette.php | 15 +- .../Palette/PaletteCollection.php | 10 +- .../Palette/PaletteCollectionInterface.php | 16 +- src/DataDefinition/Palette/Property.php | 26 +- .../Palette/PropertyInterface.php | 45 +- src/DataDefinitionContainer.php | 11 +- src/DataDefinitionContainerInterface.php | 9 +- src/DefaultEnvironment.php | 52 +- .../CcaDcGeneralExtension.php | 7 +- .../Compiler/AddSessionBagsPass.php | 7 +- src/EnvironmentInterface.php | 39 +- ...bstractEventDrivenEnvironmentPopulator.php | 8 +- .../ParentEnforcingListener.php | 18 +- .../TreeEnforcingListener.php | 33 +- src/Factory/DcGeneralFactory.php | 104 ++-- src/Factory/DcGeneralFactoryInterface.php | 29 +- src/Panel/AbstractElement.php | 42 +- src/Panel/DefaultFilterElement.php | 54 +- src/Panel/DefaultLimitElement.php | 62 ++- src/Panel/DefaultPanel.php | 12 +- src/Panel/DefaultPanelContainer.php | 33 +- src/Panel/DefaultSearchElement.php | 69 +-- src/Panel/DefaultSortElement.php | 70 +-- src/Panel/DefaultSubmitElement.php | 8 +- src/Panel/PanelContainerInterface.php | 23 +- src/Panel/PanelElementInterface.php | 21 +- src/Panel/PanelInterface.php | 29 +- src/Panel/SortElementInterface.php | 12 +- .../config/contao/picker_provider.yml | 6 +- .../contao/templates/dcbe_general_edit.html5 | 2 +- .../contao/templates/dcbe_general_show.html5 | 2 +- .../AbstractEnvironmentAwareHandler.php | 4 +- ...AbstractPropertyOverrideEditAllHandler.php | 64 ++- .../AbstractPropertyVisibilityHandler.php | 6 +- src/View/ViewTemplateInterface.php | 13 +- tests/Data/DefaultDataProviderTest.php | 37 +- 223 files changed, 6162 insertions(+), 3309 deletions(-) create mode 100644 src/DataDefinition/Palette/Condition/Property/PropertyBooleanConditionTrait.php diff --git a/.composer-require-checker.json b/.composer-require-checker.json index ae355952..0fb1f54b 100644 --- a/.composer-require-checker.json +++ b/.composer-require-checker.json @@ -1,11 +1,12 @@ { "symbol-whitelist": [ "array", "bool", "false", "int", "mixed", "null", "self", "static", "parent", "string", "true", "void", - "ContaoTwigInitializeEvent", + "ContaoCommunityAlliance\\Contao\\EventDispatcher\\CcaEventDispatcherBundle", "ContaoCommunityAlliance\\DcGeneral\\Contao\\View\\Contao2BackendView\\Exception\\EditOnlyModeException", "ContaoCommunityAlliance\\DcGeneral\\Contao\\View\\Contao2BackendView\\Exception\\NotCreatableException", "ContaoCommunityAlliance\\DcGeneral\\Contao\\View\\Contao2BackendView\\Exception\\NotDeletableException", - "ContaoCommunityAlliance\\Contao\\EventDispatcher\\CcaEventDispatcherBundle", + "ContaoCommunityAlliance\\DcGeneral\\DataDefinition\\Palette\\Condition\\Property\\PropertyBooleanConditionTrait", + "ContaoTwigInitializeEvent", "Contao\\Calendar", "Contao\\CalendarEventsModel", "Contao\\CalendarModel", @@ -18,6 +19,7 @@ "Contao\\NewsArchiveModel", "Contao\\NewsModel", "FOS\\HttpCache\\CacheInvalidator", - "MenAtWork\\MultiColumnWizardBundle\\Event\\GetOptionsEvent" + "MenAtWork\\MultiColumnWizardBundle\\Event\\GetOptionsEvent", + "Symfony\\Cmf\\Component\\Routing\\ChainRouterInterface" ] } diff --git a/.github/workflows/diagnostics.yml b/.github/workflows/diagnostics.yml index f6c755d9..bddb943b 100644 --- a/.github/workflows/diagnostics.yml +++ b/.github/workflows/diagnostics.yml @@ -12,7 +12,7 @@ jobs: strategy: fail-fast: false matrix: - php: [ '7.4', '8.0', '8.1', '8.2' ] + php: [ '8.1', '8.2' ] contao: [ '~4.13.0' ] phpcq_install: [ 'update' ] output: [ '-o github-action -o default' ] diff --git a/.phpcq.yaml.dist b/.phpcq.yaml.dist index ed969b08..8a8013bc 100644 --- a/.phpcq.yaml.dist +++ b/.phpcq.yaml.dist @@ -56,6 +56,7 @@ phpcq: # phpmd - 0F9684B8B16B7AB0 - A4E55EA12C7C085C + - 9093F8B32E4815AA # composer-require-checker - 033E5F8D801A2F8D diff --git a/composer.json b/composer.json index 01b6afaf..d9de59be 100644 --- a/composer.json +++ b/composer.json @@ -32,7 +32,7 @@ "source": "https://github.com/contao-community-alliance/dc-general" }, "require": { - "php": "^7.4 || ^8.0", + "php": "^8.1", "ext-json": "*", "ext-pdo": "*", "contao-community-alliance/events-contao-bindings": "^4.13", diff --git a/src/Clipboard/ClipboardInterface.php b/src/Clipboard/ClipboardInterface.php index 1e76d964..45470c93 100644 --- a/src/Clipboard/ClipboardInterface.php +++ b/src/Clipboard/ClipboardInterface.php @@ -114,7 +114,7 @@ public function hasId(ModelIdInterface $modelId); * * @param FilterInterface $filter An item filter. * - * @return ItemInterface[] + * @return list */ public function fetch(FilterInterface $filter); diff --git a/src/Clipboard/Filter.php b/src/Clipboard/Filter.php index 9a8a9acc..a3115ad0 100644 --- a/src/Clipboard/Filter.php +++ b/src/Clipboard/Filter.php @@ -22,7 +22,7 @@ namespace ContaoCommunityAlliance\DcGeneral\Clipboard; -use ContaoCommunityAlliance\DcGeneral\Data\ModelId; +use ContaoCommunityAlliance\DcGeneral\Data\ModelIdInterface; use Symfony\Component\ExpressionLanguage\ExpressionLanguage; use function count; @@ -252,11 +252,11 @@ private function modelIsNotFromProvider($conjunction, $modelProviderName) /** * And model is. * - * @param ModelId $modelId The model id. + * @param ModelIdInterface $modelId The model id. * * @return static */ - public function andModelIs(ModelId $modelId) + public function andModelIs(ModelIdInterface $modelId) { $this->modelIs('and', $modelId); @@ -266,11 +266,11 @@ public function andModelIs(ModelId $modelId) /** * Or model is. * - * @param ModelId $modelId The model id. + * @param ModelIdInterface $modelId The model id. * * @return static */ - public function orModelIs(ModelId $modelId) + public function orModelIs(ModelIdInterface $modelId) { $this->modelIs('or', $modelId); @@ -280,12 +280,12 @@ public function orModelIs(ModelId $modelId) /** * Add model is. * - * @param string $conjunction AND or OR. - * @param ModelId $modelId The model id. + * @param string $conjunction AND or OR. + * @param ModelIdInterface $modelId The model id. * * @return static */ - private function modelIs($conjunction, ModelId $modelId) + private function modelIs($conjunction, ModelIdInterface $modelId) { if (!empty($this->expression)) { $this->expression[] = $conjunction; @@ -302,11 +302,11 @@ private function modelIs($conjunction, ModelId $modelId) /** * And model is not. * - * @param ModelId $modelId The model id. + * @param ModelIdInterface $modelId The model id. * * @return static */ - public function andModelIsNot(ModelId $modelId) + public function andModelIsNot(ModelIdInterface $modelId) { $this->modelIsNot('and', $modelId); @@ -316,11 +316,11 @@ public function andModelIsNot(ModelId $modelId) /** * Or model is not. * - * @param ModelId $modelId The model id. + * @param ModelIdInterface $modelId The model id. * * @return static */ - public function orModelIsNot(ModelId $modelId) + public function orModelIsNot(ModelIdInterface $modelId) { $this->modelIsNot('or', $modelId); @@ -330,12 +330,12 @@ public function orModelIsNot(ModelId $modelId) /** * Add model is not. * - * @param string $conjunction AND or OR. - * @param ModelId $modelId The model id. + * @param string $conjunction AND or OR. + * @param ModelIdInterface $modelId The model id. * * @return static */ - private function modelIsNot($conjunction, ModelId $modelId) + private function modelIsNot($conjunction, ModelIdInterface $modelId) { if (!empty($this->expression)) { $this->expression[] = $conjunction; @@ -495,11 +495,11 @@ private function hasNoParent($conjunction) /** * And parent is. * - * @param ModelId $parentModelId The parent id. + * @param ModelIdInterface $parentModelId The parent id. * * @return static */ - public function andParentIs(ModelId $parentModelId) + public function andParentIs(ModelIdInterface $parentModelId) { $this->parentIs('and', $parentModelId); @@ -509,11 +509,11 @@ public function andParentIs(ModelId $parentModelId) /** * Or parent is. * - * @param ModelId $parentModelId The parent id. + * @param ModelIdInterface $parentModelId The parent id. * * @return static */ - public function orParentIs(ModelId $parentModelId) + public function orParentIs(ModelIdInterface $parentModelId) { $this->parentIs('or', $parentModelId); @@ -523,12 +523,12 @@ public function orParentIs(ModelId $parentModelId) /** * Add parent is. * - * @param string $conjunction AND or OR. - * @param ModelId $parentModelId The parent id. + * @param string $conjunction AND or OR. + * @param ModelIdInterface $parentModelId The parent id. * * @return static */ - private function parentIs($conjunction, ModelId $parentModelId) + private function parentIs($conjunction, ModelIdInterface $parentModelId) { if (!empty($this->expression)) { $this->expression[] = $conjunction; @@ -545,7 +545,7 @@ private function parentIs($conjunction, ModelId $parentModelId) /** * And parent is in. * - * @param array|ModelId[] $parentModelIds The parent ids. + * @param array|ModelIdInterface[] $parentModelIds The parent ids. * * @return static */ @@ -559,7 +559,7 @@ public function andParentIsIn(array $parentModelIds) /** * Or parent is in. * - * @param array|ModelId[] $parentModelIds The parent ids. + * @param array|ModelIdInterface[] $parentModelIds The parent ids. * * @return static */ @@ -574,7 +574,7 @@ public function orParentIsIn(array $parentModelIds) * Add parent is in. * * @param string $conjunction AND or OR. - * @param array|ModelId[] $parentModelIds The parent ids. + * @param array|ModelIdInterface[] $parentModelIds The parent ids. * * @return static */ @@ -599,11 +599,11 @@ private function parentIsIn($conjunction, array $parentModelIds) /** * And parent is not. * - * @param ModelId $parentModelId The parent id. + * @param ModelIdInterface $parentModelId The parent id. * * @return static */ - public function andParentIsNot(ModelId $parentModelId) + public function andParentIsNot(ModelIdInterface $parentModelId) { $this->parentIsNot('and', $parentModelId); @@ -613,11 +613,11 @@ public function andParentIsNot(ModelId $parentModelId) /** * Or parent is not. * - * @param ModelId $parentModelId The parent id. + * @param ModelIdInterface $parentModelId The parent id. * * @return static */ - public function orParentIsNot(ModelId $parentModelId) + public function orParentIsNot(ModelIdInterface $parentModelId) { $this->parentIsNot('and', $parentModelId); @@ -627,12 +627,12 @@ public function orParentIsNot(ModelId $parentModelId) /** * Add parent is not. * - * @param string $conjunction AND or OR. - * @param ModelId $parentModelId The parent id. + * @param string $conjunction AND or OR. + * @param ModelIdInterface $parentModelId The parent id. * * @return static */ - private function parentIsNot($conjunction, ModelId $parentModelId) + private function parentIsNot($conjunction, ModelIdInterface $parentModelId) { if (!empty($this->expression)) { $this->expression[] = $conjunction; @@ -649,7 +649,7 @@ private function parentIsNot($conjunction, ModelId $parentModelId) /** * And parent is not in. * - * @param array|ModelId[] $parentModelIds The parent ids. + * @param array|ModelIdInterface[] $parentModelIds The parent ids. * * @return static */ @@ -663,7 +663,7 @@ public function andParentIsNotIn(array $parentModelIds) /** * Or parent is not in. * - * @param array|ModelId[] $parentModelIds The parent ids. + * @param array|ModelIdInterface[] $parentModelIds The parent ids. * * @return static */ @@ -678,7 +678,7 @@ public function orParentIsNotIn(array $parentModelIds) * Add parent is not in. * * @param string $conjunction AND or OR. - * @param array|ModelId[] $parentModelIds The parent ids. + * @param array|ModelIdInterface[] $parentModelIds The parent ids. * * @return static */ diff --git a/src/Contao/Callback/ModelLabelCallbackListener.php b/src/Contao/Callback/ModelLabelCallbackListener.php index c4e478ba..59db1cc6 100644 --- a/src/Contao/Callback/ModelLabelCallbackListener.php +++ b/src/Contao/Callback/ModelLabelCallbackListener.php @@ -62,6 +62,7 @@ public function update($event, $value) if (!\is_array($value)) { return; } + /** @var list $value */ $this->updateTableMode($event, $value); } @@ -102,12 +103,12 @@ private function updateNonTableMode(ModelToLabelEvent $event, ?string $value): v /** * Set the value in the event. * - * @param ModelToLabelEvent $event The event being emitted. - * @param array $arguments The label arguments. + * @param ModelToLabelEvent $event The event being emitted. + * @param string|list $arguments The label arguments. * * @return void */ - private function updateTableMode(ModelToLabelEvent $event, array $arguments): void + private function updateTableMode(ModelToLabelEvent $event, array|string $arguments): void { if (empty($arguments)) { return; diff --git a/src/Contao/Callback/PropertyInputFieldGetWizardCallbackListener.php b/src/Contao/Callback/PropertyInputFieldGetWizardCallbackListener.php index 31d12da9..56d46ae5 100644 --- a/src/Contao/Callback/PropertyInputFieldGetWizardCallbackListener.php +++ b/src/Contao/Callback/PropertyInputFieldGetWizardCallbackListener.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2020 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,13 +14,15 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2020 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ namespace ContaoCommunityAlliance\DcGeneral\Contao\Callback; +use Contao\Widget; use ContaoCommunityAlliance\DcGeneral\Contao\Compatibility\DcCompat; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\BuildWidgetEvent; @@ -50,6 +52,9 @@ public function getArgs($event) */ public function update($event, $value) { - $event->getWidget()->wizard .= $value; + $widget = $event->getWidget(); + if ($widget instanceof Widget) { + $widget->wizard .= $value; + } } } diff --git a/src/Contao/Callback/PropertyInputFieldGetXLabelCallbackListener.php b/src/Contao/Callback/PropertyInputFieldGetXLabelCallbackListener.php index 5449ef86..044d1e11 100644 --- a/src/Contao/Callback/PropertyInputFieldGetXLabelCallbackListener.php +++ b/src/Contao/Callback/PropertyInputFieldGetXLabelCallbackListener.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2020 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,13 +13,15 @@ * @package contao-community-alliance/dc-general * @author Christian Schiffler * @author Sven Baumann - * @copyright 2013-2020 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ namespace ContaoCommunityAlliance\DcGeneral\Contao\Callback; +use Contao\Widget; use ContaoCommunityAlliance\DcGeneral\Contao\Compatibility\DcCompat; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\BuildWidgetEvent; @@ -49,6 +51,9 @@ public function getArgs($event) */ public function update($event, $value) { - $event->getWidget()->xlabel .= $value; + $widget = $event->getWidget(); + if ($widget instanceof Widget) { + $widget->wizard .= $value; + } } } diff --git a/src/Contao/Compatibility/DcCompat.php b/src/Contao/Compatibility/DcCompat.php index a1d08c0f..6d1fefa8 100644 --- a/src/Contao/Compatibility/DcCompat.php +++ b/src/Contao/Compatibility/DcCompat.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -30,7 +31,7 @@ use ContaoCommunityAlliance\DcGeneral\Factory\Event\PopulateEnvironmentEvent; /** - * Small compatibility layer for callbacks, that expect a "full featured" DC instance. + * Small compatibility layer for callbacks, that expect a "full-featured" DC instance. */ class DcCompat extends General { diff --git a/src/Contao/Dca/Builder/Legacy/LegacyDcaDataDefinitionBuilder.php b/src/Contao/Dca/Builder/Legacy/LegacyDcaDataDefinitionBuilder.php index fc683e59..9c35455e 100644 --- a/src/Contao/Dca/Builder/Legacy/LegacyDcaDataDefinitionBuilder.php +++ b/src/Contao/Dca/Builder/Legacy/LegacyDcaDataDefinitionBuilder.php @@ -876,8 +876,10 @@ protected function parseListSorting(ListingConfigInterface $listing, array $list $parsedProperties = []; $sortingDca = ($listDca['sorting'] ?? []); - if (isset($sortingDca['headerFields'])) { - $listing->setHeaderPropertyNames((array) $sortingDca['headerFields']); + if ($headerFields = ($sortingDca['headerFields'] ?? [])) { + assert(\is_array($headerFields)); + /** @var list $headerFields */ + $listing->setHeaderPropertyNames($headerFields); } if (isset($sortingDca['icon'])) { diff --git a/src/Contao/Dca/Populator/BackendViewPopulator.php b/src/Contao/Dca/Populator/BackendViewPopulator.php index 01a39f4e..977a29f8 100644 --- a/src/Contao/Dca/Populator/BackendViewPopulator.php +++ b/src/Contao/Dca/Populator/BackendViewPopulator.php @@ -75,6 +75,8 @@ public function __construct( ) { if (null === $tokenManager) { $tokenManager = System::getContainer()->get('security.csrf.token_manager'); + assert($tokenManager instanceof CsrfTokenManagerInterface); + // @codingStandardsIgnoreStart @trigger_error( 'Not passing the csrf token manager as 2th argument to "' . __METHOD__ . '" is deprecated ' . @@ -85,6 +87,8 @@ public function __construct( } if (null === $tokenName) { $tokenName = System::getContainer()->getParameter('contao.csrf_token_name'); + assert(\is_string($tokenName)); + // @codingStandardsIgnoreStart @trigger_error( 'Not passing the csrf token name as 3th argument to "' . __METHOD__ . '" is deprecated ' . diff --git a/src/Contao/Dca/Populator/HardCodedPopulator.php b/src/Contao/Dca/Populator/HardCodedPopulator.php index 8ce72df1..f7d4ff57 100644 --- a/src/Contao/Dca/Populator/HardCodedPopulator.php +++ b/src/Contao/Dca/Populator/HardCodedPopulator.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -71,11 +72,15 @@ public function populateController(EnvironmentInterface $environment) public function populate(EnvironmentInterface $environment) { if (!$environment->getSessionStorage()) { - /** @var SessionStorageFactory $sessionFactory */ $sessionFactory = System::getContainer()->get('cca.dc-general.session_factory'); - $definition = $environment->getDataDefinition(); + assert($sessionFactory instanceof SessionStorageFactory); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + $sessionStorage = $sessionFactory->createService(); assert($definition instanceof ContainerInterface); + $sessionStorage->setScope('DC_GENERAL_' . \strtoupper($definition->getName())); $environment->setSessionStorage($sessionStorage); // @codingStandardsIgnoreStart diff --git a/src/Contao/Factory/SessionStorageFactory.php b/src/Contao/Factory/SessionStorageFactory.php index 0247535a..bec33a78 100644 --- a/src/Contao/Factory/SessionStorageFactory.php +++ b/src/Contao/Factory/SessionStorageFactory.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -12,7 +12,8 @@ * * @package contao-community-alliance/dc-general * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -55,7 +56,7 @@ public function createService() $session = $this->container->get('session'); assert($session instanceof SessionInterface); $keys = $this->container->getParameter('cca.dc-general.session.database_keys'); - assert(is_array($keys)); + assert(\is_array($keys)); /** @var list $keys */ return new SessionStorage($session, $keys); diff --git a/src/Contao/Picker/PagePickerProvider.php b/src/Contao/Picker/PagePickerProvider.php index 52bbfe42..35a7563e 100644 --- a/src/Contao/Picker/PagePickerProvider.php +++ b/src/Contao/Picker/PagePickerProvider.php @@ -22,12 +22,46 @@ use Contao\CoreBundle\Picker\AbstractPickerProvider; use Contao\CoreBundle\Picker\DcaPickerProviderInterface; use Contao\CoreBundle\Picker\PickerConfig; +use Contao\System; +use Knp\Menu\FactoryInterface; +use Symfony\Component\Routing\RouterInterface; +use Symfony\Component\Security\Core\Security; +use Symfony\Contracts\Translation\TranslatorInterface; /** * Provides the page picker. */ class PagePickerProvider extends AbstractPickerProvider implements DcaPickerProviderInterface { + private Security $security; + + /** + * @internal + */ + public function __construct( + FactoryInterface $menuFactory, + RouterInterface $router, + ?TranslatorInterface $translator, + ?Security $security + ) { + parent::__construct($menuFactory, $router, $translator); + + if (null === $security) { + $security = System::getContainer()->get('security.helper'); + assert($security instanceof Security); + + // @codingStandardsIgnoreStart + @trigger_error( + 'Not passing the security as argument to "' . __METHOD__ . '" is deprecated ' . + 'and will cause an error in DCG 3.0', + E_USER_DEPRECATED + ); + // @codingStandardsIgnoreEnd + } + + $this->security = $security; + } + /** * {@inheritdoc} */ @@ -41,7 +75,8 @@ public function getName() */ public function supportsContext($context) { - return \in_array($context, ['cca_ page', 'cca_link'], true) && $this->getUser()->hasAccess('page', 'modules'); + return \in_array($context, ['cca_page', 'cca_link'], true) + && $this->security->isGranted('contao_user.modules', 'page'); } /** diff --git a/src/Contao/SessionStorage.php b/src/Contao/SessionStorage.php index d5354c70..b6d7956a 100644 --- a/src/Contao/SessionStorage.php +++ b/src/Contao/SessionStorage.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2022 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2022 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -22,6 +23,7 @@ namespace ContaoCommunityAlliance\DcGeneral\Contao; use ContaoCommunityAlliance\DcGeneral\SessionStorageInterface; +use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBagInterface; use Symfony\Component\HttpFoundation\Session\SessionInterface; /** @@ -32,9 +34,9 @@ class SessionStorage implements SessionStorageInterface /** * The session scope. * - * @var string + * @var null|string */ - private $scope; + private ?string $scope = null; /** * The symfony session. @@ -99,7 +101,6 @@ public function setScope($scope) // @codingStandardsIgnoreStart @\trigger_error('The scope can not be change! Use a new session storage.', E_USER_ERROR); // @codingStandardsIgnoreEnd - return; } $this->scope = $scope; @@ -175,6 +176,7 @@ public function clear() $this->load(); $this->attributes = []; $this->persist(); + return $this; } /** @@ -190,11 +192,13 @@ private function load() $sessionBag = $this->session->getBag($this->getSessionBagKey()); $databaseSessionBag = $this->session->getBag($this->getDatabaseSessionBagKey()); + assert($sessionBag instanceof AttributeBagInterface); + assert($databaseSessionBag instanceof AttributeBagInterface); - $this->attributes = \array_merge( - (array) $sessionBag->get($this->getScope()), - (array) $databaseSessionBag->get($this->getScope()) - ); + if (null === $scope = $this->getScope()) { + return; + } + $this->attributes = \array_merge((array) $sessionBag->get($scope), (array) $databaseSessionBag->get($scope)); } /** @@ -204,11 +208,17 @@ private function load() */ private function persist() { + if (null === $scope = $this->getScope()) { + return; + } + $sessionBag = $this->session->getBag($this->getSessionBagKey()); - $sessionBag->set($this->getScope(), $this->filterAttributes()); + assert($sessionBag instanceof AttributeBagInterface); + $sessionBag->set($scope, $this->filterAttributes()); $databaseSessionBag = $this->session->getBag($this->getDatabaseSessionBagKey()); - $databaseSessionBag->set($this->getScope(), $this->filterAttributes(true)); + assert($databaseSessionBag instanceof AttributeBagInterface); + $databaseSessionBag->set($scope, $this->filterAttributes(true)); } /** @@ -221,10 +231,10 @@ private function persist() */ private function filterAttributes($determineDatabase = false) { - $databaseAttributes = \array_merge( - $this->databaseKeys['common'] ?? [], - $this->databaseKeys[$this->getScope()] ?? [] - ); + $databaseAttributes = $this->databaseKeys['common'] ?? []; + if ($scope = $this->getScope()) { + $databaseAttributes = \array_merge($databaseAttributes, $this->databaseKeys[$scope] ?? []); + } if ($determineDatabase) { return \array_intersect_key($this->attributes, \array_flip($databaseAttributes)); diff --git a/src/Contao/Subscriber/DynamicParentTableSubscriber.php b/src/Contao/Subscriber/DynamicParentTableSubscriber.php index 017a3ab4..0667b7d0 100644 --- a/src/Contao/Subscriber/DynamicParentTableSubscriber.php +++ b/src/Contao/Subscriber/DynamicParentTableSubscriber.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,8 @@ * @package contao-community-alliance/dc-general * @author Christian Schiffler * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -44,7 +45,7 @@ class DynamicParentTableSubscriber implements EventSubscriberInterface * * array('eventName' => array('methodName', $priority)) * * array('eventName' => array(array('methodName1', $priority), array('methodName2')) * - * @return array The event names to listen to. + * @return array The event names to listen to. */ public static function getSubscribedEvents() { @@ -62,11 +63,12 @@ public static function getSubscribedEvents() */ public function handlePrePersistModelEvent(PrePersistModelEvent $event) { - $enviroment = $event->getEnvironment(); - $dataDefinition = $enviroment->getDataDefinition(); + $environment = $event->getEnvironment(); + $dataDefinition = $environment->getDataDefinition(); if ( - null === ($parentDataDefinition = $enviroment->getParentDataDefinition()) + null === $dataDefinition + || null === ($parentDataDefinition = $environment->getParentDataDefinition()) || (false === $dataDefinition->getPropertiesDefinition()->hasProperty('ptable')) || (false === $dataDefinition->getBasicDefinition()->isDynamicParentTable()) ) { diff --git a/src/Contao/Subscriber/FallbackResetSubscriber.php b/src/Contao/Subscriber/FallbackResetSubscriber.php index da4222c5..599c7cf5 100644 --- a/src/Contao/Subscriber/FallbackResetSubscriber.php +++ b/src/Contao/Subscriber/FallbackResetSubscriber.php @@ -24,7 +24,10 @@ use ContaoCommunityAlliance\DcGeneral\Controller\ModelCollector; use ContaoCommunityAlliance\DcGeneral\Data\ConfigInterface; +use ContaoCommunityAlliance\DcGeneral\Data\DataProviderInterface; +use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; use ContaoCommunityAlliance\DcGeneral\Data\ModelManipulator; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\Event\AbstractModelAwareEvent; use ContaoCommunityAlliance\DcGeneral\Event\PostDuplicateModelEvent; use ContaoCommunityAlliance\DcGeneral\Event\PostPersistModelEvent; @@ -51,7 +54,7 @@ class FallbackResetSubscriber implements EventSubscriberInterface * * array('eventName' => array('methodName', $priority)) * * array('eventName' => array(array('methodName1', $priority), array('methodName2')) * - * @return array The event names to listen to. + * @return array The event names to listen to. */ public static function getSubscribedEvents() { @@ -91,21 +94,32 @@ public function handlePostDuplicateModelEvent(PostDuplicateModelEvent $event) * @param AbstractModelAwareEvent $event The event. * * @return void + * + * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ private function handleFallback(AbstractModelAwareEvent $event) { - $model = $event->getModel(); + $model = $event->getModel(); + assert($model instanceof ModelInterface); + $dataProvider = $event->getEnvironment()->getDataProvider($model->getProviderName()); - $properties = $event->getEnvironment()->getDataDefinition()->getPropertiesDefinition(); + assert($dataProvider instanceof DataProviderInterface); + + $definition = $event->getEnvironment()->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $properties = $definition->getPropertiesDefinition(); foreach (\array_keys($model->getPropertiesAsArray()) as $propertyName) { if (!$properties->hasProperty($propertyName)) { continue; } + $property = $properties->getProperty($propertyName); - $extra = (array) $property->getExtra(); + $extra = $property->getExtra(); + if (\array_key_exists('fallback', $extra) && (true === $extra['fallback'])) { - // BC Layer - use old reset fallback methodology until it get's removed. + // BC Layer - use old reset fallback methodology until it gets removed. if (null === ($config = $this->determineFilterConfig($event))) { // @codingStandardsIgnoreStart @\trigger_error( @@ -114,8 +128,10 @@ private function handleFallback(AbstractModelAwareEvent $event) E_USER_DEPRECATED ); // @codingStandardsIgnoreEnd - + /** @psalm-suppress DeprecatedMethod */ $dataProvider->resetFallback($propertyName); + $dataProvider->save($model); + continue; } // If value is empty, no need to reset the fallback. @@ -126,9 +142,14 @@ private function handleFallback(AbstractModelAwareEvent $event) $models = $dataProvider->fetchAll($config); foreach ($models as $resetModel) { + if (!($resetModel instanceof ModelInterface)) { + continue; + } + if ($model->getId() === $resetModel->getId()) { continue; } + $resetModel->setProperty($propertyName, ModelManipulator::sanitizeValue($property, null)); $dataProvider->save($resetModel); } @@ -142,13 +163,23 @@ private function handleFallback(AbstractModelAwareEvent $event) * @param AbstractModelAwareEvent $event The event. * * @return ConfigInterface|null + * + * @SuppressWarnings(PHPMD.CyclomaticComplexity) + * @SuppressWarnings(NPathComplexity) */ private function determineFilterConfig(AbstractModelAwareEvent $event) { $environment = $event->getEnvironment(); $model = $event->getModel(); - $dataProvider = $environment->getDataProvider($model->getProviderName()); - $definition = $environment->getDataDefinition(); + + if (null === ($dataProvider = $environment->getDataProvider($model->getProviderName()))) { + return null; + } + + if (null === ($definition = $environment->getDataDefinition())) { + return null; + } + $relationship = $definition->getModelRelationshipDefinition(); $root = $relationship->getRootCondition(); @@ -156,8 +187,12 @@ private function determineFilterConfig(AbstractModelAwareEvent $event) return $dataProvider->getEmptyConfig()->setFilter($root->getFilterArray()); } + if (null === ($parentDefinition = $definition->getBasicDefinition()->getParentDataProvider())) { + return null; + } + $parentFilter = $relationship->getChildCondition( - $definition->getBasicDefinition()->getParentDataProvider(), + $parentDefinition, $model->getProviderName() ); diff --git a/src/Contao/Subscriber/FormatModelLabelSubscriber.php b/src/Contao/Subscriber/FormatModelLabelSubscriber.php index 449bf3a1..c08a06ee 100644 --- a/src/Contao/Subscriber/FormatModelLabelSubscriber.php +++ b/src/Contao/Subscriber/FormatModelLabelSubscriber.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2022 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -15,7 +15,7 @@ * @author Tristan Lins * @author Sven Baumann * @author Ingolf Steinhardt - * @copyright 2013-2022 Contao Community Alliance. + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -27,6 +27,7 @@ use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\ModelToLabelEvent; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\ViewHelpers; use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\PropertiesDefinitionInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\GroupAndSortingDefinitionInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\GroupAndSortingInformationInterface; @@ -57,6 +58,8 @@ public function handleFormatModelLabel(FormatModelLabelEvent $event) $model = $event->getModel(); $dataDefinition = $environment->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + /** @var Contao2BackendViewDefinitionInterface $viewSection */ $viewSection = $dataDefinition->getDefinition(Contao2BackendViewDefinitionInterface::NAME); $listing = $viewSection->getListingConfig(); @@ -72,7 +75,11 @@ public function handleFormatModelLabel(FormatModelLabelEvent $event) ->setLabel($formatter->getFormat()) ->setFormatter($formatter); - $environment->getEventDispatcher()->dispatch($modelToLabelEvent, ModelToLabelEvent::NAME); + if (null === ($dispatcher = $environment->getEventDispatcher())) { + return; + } + + $dispatcher->dispatch($modelToLabelEvent, ModelToLabelEvent::NAME); // Add columns. if ($listing->getShowColumns()) { @@ -126,7 +133,7 @@ private function getFirstSorting(GroupAndSortingDefinitionInterface $sortingDefi * @param EnvironmentInterface $environment The environment. * @param ModelInterface $model The model. * - * @return array + * @return array */ private function prepareLabelArguments( $propertyNames, @@ -155,13 +162,13 @@ private function prepareLabelArguments( /** * Render for column layout. * - * @param string[] $propertyNames The properties. - * @param string[] $args The rendered arguments. - * @param string $firstSorting The sorting column. + * @param string[] $propertyNames The properties. + * @param string|array $args The rendered arguments. + * @param string $firstSorting The sorting column. * * @return array */ - private function renderWithColumns($propertyNames, $args, $firstSorting) + private function renderWithColumns(array $propertyNames, array|string $args, string $firstSorting) { $label = []; if (!\is_array($args)) { @@ -194,13 +201,13 @@ private function renderWithColumns($propertyNames, $args, $firstSorting) /** * Render as single value. * - * @param string $label The label string. - * @param string[] $args The rendered arguments. - * @param null $maxLength The maximum length for the label or null to allow unlimited. + * @param string $label The label string. + * @param string|array $args The rendered arguments. + * @param null|int $maxLength The maximum length for the label or null to allow unlimited. * * @return string */ - private function renderSingleValue($label, $args, $maxLength = null) + private function renderSingleValue(string $label, array|string $args, ?int $maxLength = null) { // BC: sometimes the label was returned as string in the arguments instead of an array. $string = !\is_array($args) ? $args : \vsprintf($label, $args); diff --git a/src/Contao/View/Contao2BackendView/ActionHandler/AbstractListShowAllHandler.php b/src/Contao/View/Contao2BackendView/ActionHandler/AbstractListShowAllHandler.php index 5821695d..4ee49f1e 100644 --- a/src/Contao/View/Contao2BackendView/ActionHandler/AbstractListShowAllHandler.php +++ b/src/Contao/View/Contao2BackendView/ActionHandler/AbstractListShowAllHandler.php @@ -23,19 +23,18 @@ namespace ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\ActionHandler; -use Contao\Backend; -use Contao\Environment; -use Contao\Message; -use Contao\StringUtil; -use Contao\System; use ContaoCommunityAlliance\Contao\Bindings\ContaoEvents; use ContaoCommunityAlliance\Contao\Bindings\Events\Backend\AddToUrlEvent; use ContaoCommunityAlliance\Contao\Bindings\Events\Image\GenerateHtmlEvent; use ContaoCommunityAlliance\DcGeneral\Action; +use ContaoCommunityAlliance\DcGeneral\BaseConfigRegistry; +use ContaoCommunityAlliance\DcGeneral\Clipboard\ClipboardInterface; use ContaoCommunityAlliance\DcGeneral\Clipboard\Filter; +use ContaoCommunityAlliance\DcGeneral\Clipboard\FilterInterface; use ContaoCommunityAlliance\DcGeneral\Contao\DataDefinition\Definition\Contao2BackendViewDefinitionInterface; use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminator; use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminatorAwareTrait; +use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\BackendViewInterface; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\ButtonRenderer; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\ContaoBackendViewTemplate; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\GetBreadcrumbEvent; @@ -44,29 +43,39 @@ use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\GlobalButtonRenderer; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\PanelRenderer; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\ViewHelpers; +use ContaoCommunityAlliance\DcGeneral\Controller\ControllerInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\BasicDefinitionInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\GroupAndSortingDefinitionCollectionInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\GroupAndSortingDefinitionInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\GroupAndSortingInformationInterface; use ContaoCommunityAlliance\DcGeneral\Data\CollectionInterface; +use ContaoCommunityAlliance\DcGeneral\Data\DataProviderInterface; use ContaoCommunityAlliance\DcGeneral\Data\ModelId; use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; use ContaoCommunityAlliance\DcGeneral\Data\MultiLanguageDataProviderInterface; -use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; -use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\DefinitionInterface; -use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\GroupAndSortingDefinitionInterface; -use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\GroupAndSortingInformationInterface; use ContaoCommunityAlliance\DcGeneral\DcGeneralEvents; use ContaoCommunityAlliance\DcGeneral\DcGeneralViews; use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; use ContaoCommunityAlliance\DcGeneral\Event\ActionEvent; use ContaoCommunityAlliance\DcGeneral\Event\FormatModelLabelEvent; use ContaoCommunityAlliance\DcGeneral\Event\ViewEvent; +use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; +use ContaoCommunityAlliance\DcGeneral\Panel\PanelContainerInterface; +use ContaoCommunityAlliance\DcGeneral\SessionStorageInterface; use ContaoCommunityAlliance\DcGeneral\View\ActionHandler\CallActionTrait; use ContaoCommunityAlliance\Translator\TranslatorInterface as CcaTranslator; +use Contao\Backend; +use Contao\Environment; +use Contao\Message; +use Contao\StringUtil; +use Contao\System; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface; use Symfony\Contracts\Translation\TranslatorInterface; use function array_key_exists; use function implode; -use function in_array; -use function sprintf; use function str_replace; use function strpos; use function trigger_error; @@ -92,9 +101,9 @@ abstract class AbstractListShowAllHandler /** * The cca translator. * - * @var CcaTranslator|TranslatorInterface + * @var CcaTranslator */ - private $ccaTranslator; + private CcaTranslator $ccaTranslator; /** * The token manager. @@ -113,11 +122,11 @@ abstract class AbstractListShowAllHandler /** * AbstractHandler constructor. * - * @param RequestScopeDeterminator $scopeDeterminator The request mode determinator. - * @param TranslatorInterface $translator The translator. - * @param CcaTranslator|TranslatorInterface $ccaTranslator The cca translator. - * @param CsrfTokenManagerInterface|null $tokenManager The token manager. - * @param string|null $tokenName The token name. + * @param RequestScopeDeterminator $scopeDeterminator The request mode determinator. + * @param TranslatorInterface $translator The translator. + * @param CcaTranslator $ccaTranslator The cca translator. + * @param CsrfTokenManagerInterface|null $tokenManager The token manager. + * @param string|null $tokenName The token name. */ public function __construct( RequestScopeDeterminator $scopeDeterminator, @@ -133,6 +142,8 @@ public function __construct( if (null === $tokenManager) { $tokenManager = System::getContainer()->get('security.csrf.token_manager'); + assert($tokenManager instanceof CsrfTokenManagerInterface); + // @codingStandardsIgnoreStart @trigger_error( 'Not passing the csrf token manager as 4th argument to "' . __METHOD__ . '" is deprecated ' . @@ -143,6 +154,8 @@ public function __construct( } if (null === $tokenName) { $tokenName = System::getContainer()->getParameter('contao.csrf_token_name'); + assert(\is_string($tokenName)); + // @codingStandardsIgnoreStart @trigger_error( 'Not passing the csrf token name as 5th argument to "' . __METHOD__ . '" is deprecated ' . @@ -152,8 +165,8 @@ public function __construct( // @codingStandardsIgnoreEnd } - $this->tokenManager = $tokenManager; - $this->tokenName = $tokenName; + $this->tokenManager = $tokenManager; + $this->tokenName = $tokenName; } /** @@ -170,18 +183,23 @@ public function handleEvent(ActionEvent $event) } $environment = $event->getEnvironment(); - $basic = $environment->getDataDefinition()->getBasicDefinition(); - $action = $event->getAction(); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $basic = $definition->getBasicDefinition(); + $action = $event->getAction(); if ( - null !== $event->getResponse() + (null === $mode = $basic->getMode()) + || null !== $event->getResponse() || ('showAll' !== $action->getName()) - || !$this->wantToHandle($basic->getMode(), $action) + || !$this->wantToHandle($mode, $action) ) { return; } - if (false !== ($response = $this->process($action, $environment))) { + if (null !== ($response = $this->process($action, $environment))) { $event->setResponse($response); } } @@ -192,12 +210,15 @@ public function handleEvent(ActionEvent $event) * @param Action $action The action being handled. * @param EnvironmentInterface $environment Current dc-general environment. * - * @return string + * @return string|null */ protected function process(Action $action, EnvironmentInterface $environment) { + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + // Edit only mode, forward to edit action. - if ($environment->getDataDefinition()->getBasicDefinition()->isEditOnlyMode()) { + if ($definition->getBasicDefinition()->isEditOnlyMode()) { return $this->callAction($environment, 'edit', $action->getArguments()); } @@ -207,17 +228,22 @@ protected function process(Action $action, EnvironmentInterface $environment) // Process now. $collection = $this->loadCollection($environment); + assert($collection instanceof CollectionInterface); $this->handleEditAllButton($collection, $environment); - $this->renderCollection($environment, $collection, $grouping); + $this->renderCollection($environment, $collection, $grouping ?: []); - $template = $this->determineTemplate($grouping) + $template = $this->determineTemplate($grouping ?: []); + $template ->set('collection', $collection) ->set('mode', ($grouping ? $grouping['mode'] : null)) ->set('theme', Backend::getTheme()); $this->renderTemplate($template, $environment); + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + $clipboard = new ViewEvent($environment, $action, DcGeneralViews::CLIPBOARD, []); - $environment->getEventDispatcher()->dispatch($clipboard, DcGeneralEvents::VIEW); + $dispatcher->dispatch($clipboard, DcGeneralEvents::VIEW); return implode( "\n", @@ -232,7 +258,7 @@ protected function process(Action $action, EnvironmentInterface $environment) } /** - * Execute the multi language support. + * Execute the multi-language support. * * @param EnvironmentInterface $environment The environment. * @@ -247,10 +273,12 @@ private function languageSwitcher(EnvironmentInterface $environment) return ''; } - /** @var MultiLanguageDataProviderInterface $dataProvider */ + $controller = $environment->getController(); + assert($controller instanceof ControllerInterface); + /** @var MultiLanguageDataProviderInterface $dataProvider */ return $template - ->set('languages', $environment->getController()->getSupportedLanguages(null)) + ->set('languages', $controller->getSupportedLanguages(null)) ->set('language', $dataProvider->getCurrentLanguage()) ->set('submit', $this->translator->trans('MSC.showSelected', [], 'contao_default')) ->set('REQUEST_TOKEN', $this->tokenManager->getToken($this->tokenName)) @@ -262,20 +290,22 @@ private function languageSwitcher(EnvironmentInterface $environment) * * @param ContainerInterface $definition Data container definition. * - * @return DefinitionInterface|Contao2BackendViewDefinitionInterface + * @return Contao2BackendViewDefinitionInterface */ protected function getViewSection(ContainerInterface $definition) { - return $definition->getDefinition(Contao2BackendViewDefinitionInterface::NAME); + $viewDefinition = $definition->getDefinition(Contao2BackendViewDefinitionInterface::NAME); + assert($viewDefinition instanceof Contao2BackendViewDefinitionInterface); + return $viewDefinition; } /** * Check if the action should be handled. * - * @param string $mode The list mode. + * @param int $mode The list mode. * @param Action $action The action. * - * @return mixed + * @return bool */ abstract protected function wantToHandle($mode, Action $action); @@ -286,7 +316,7 @@ abstract protected function wantToHandle($mode, Action $action); * @param string|null $domain The domain name to use. * @param array $parameters Parameters. * - * @return array|string + * @return string */ protected function translate($key, $domain, array $parameters = []) { @@ -303,7 +333,11 @@ protected function translate($key, $domain, array $parameters = []) // @codingStandardsIgnoreEnd $translated = - $this->translator->trans(sprintf('%s.%s', $domain, $key), $parameters, sprintf('contao_%s', $domain)); + $this->translator->trans( + \sprintf('%s.%s', $domain ?? '', $key), + $parameters, + \sprintf('contao_%s', $domain ?? '') + ); } return $translated; @@ -319,8 +353,11 @@ protected function translate($key, $domain, array $parameters = []) */ protected function renderModel(ModelInterface $model, EnvironmentInterface $environment) { + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + $event = new FormatModelLabelEvent($environment, $model); - $environment->getEventDispatcher()->dispatch($event, DcGeneralEvents::FORMAT_MODEL_LABEL); + $dispatcher->dispatch($event, DcGeneralEvents::FORMAT_MODEL_LABEL); $model->setMeta($model::LABEL_VALUE, $event->getLabel()); } @@ -357,12 +394,17 @@ abstract protected function determineTemplate($groupingInformation); protected function renderTemplate(ContaoBackendViewTemplate $template, EnvironmentInterface $environment) { $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $provider = $environment->getInputProvider(); + assert($provider instanceof InputProviderInterface); + $showColumn = $this->getViewSection($definition)->getListingConfig()->getShowColumns(); $template ->set('subHeadline', $this->translate('MSC.select_models', 'contao_default')) - ->set('tableName', ($definition->getName() ?? 'none')) - ->set('select', 'select' === $environment->getInputProvider()->getParameter('act')) + ->set('tableName', ($definition->getName() ?: 'none')) + ->set('select', 'select' === $provider->getParameter('act')) ->set('action', StringUtil::ampersand(Environment::get('request'))) ->set('selectButtons', $this->getSelectButtons($environment)) ->set('sortable', $this->isSortable($environment)) @@ -390,17 +432,30 @@ protected function renderTemplate(ContaoBackendViewTemplate $template, Environme * * @param EnvironmentInterface $environment The environment. * - * @return CollectionInterface + * @return CollectionInterface|list */ protected function loadCollection(EnvironmentInterface $environment) { - $dataConfig = $environment->getBaseConfigRegistry()->getBaseConfig(); - $listingConfig = $this->getViewSection($environment->getDataDefinition())->getListingConfig(); - $panel = $environment->getView()->getPanel(); + $config = $environment->getBaseConfigRegistry(); + assert($config instanceof BaseConfigRegistry); + + $view = $environment->getView(); + assert($view instanceof BackendViewInterface); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $dataConfig = $config->getBaseConfig(); + $listingConfig = $this->getViewSection($definition)->getListingConfig(); + $panel = $view->getPanel(); + assert($panel instanceof PanelContainerInterface); ViewHelpers::initializeSorting($panel, $dataConfig, $listingConfig); - return $environment->getDataProvider()->fetchAll($dataConfig); + $provider = $environment->getDataProvider(); + assert($provider instanceof DataProviderInterface); + + return $provider->fetchAll($dataConfig); } /** @@ -424,15 +479,25 @@ private function generateHeaderButtons(EnvironmentInterface $environment) * * @return void */ - private function renderCollection(EnvironmentInterface $environment, CollectionInterface $collection, $grouping) - { - $listing = $this->getViewSection($environment->getDataDefinition())->getListingConfig(); + private function renderCollection( + EnvironmentInterface $environment, + CollectionInterface $collection, + array $grouping + ) { + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $listing = $this->getViewSection($definition)->getListingConfig(); $remoteCur = null; $groupClass = 'tl_folder_tlist'; $eoCount = -1; + if (null === ($inputProvider = $environment->getInputProvider())) { + return; + } + // Generate buttons - only if not in select mode! - if ('select' !== $environment->getInputProvider()->getParameter('act')) { + if ('select' !== $inputProvider->getParameter('act')) { $buttonRenderer = new ButtonRenderer($environment); $buttonRenderer->renderButtonsForCollection($collection); } @@ -440,7 +505,7 @@ private function renderCollection(EnvironmentInterface $environment, CollectionI // Run each model. foreach ($collection as $model) { /** @var ModelInterface $model */ - $this->addGroupHeader($environment, (array) $grouping, $model, $groupClass, $eoCount, $remoteCur); + $this->addGroupHeader($environment, $grouping, $model, $groupClass, $eoCount, $remoteCur); if ($listing->getItemCssClass()) { $model->setMeta($model::CSS_CLASS, $listing->getItemCssClass()); @@ -451,7 +516,11 @@ private function renderCollection(EnvironmentInterface $environment, CollectionI $cssClasses[] = $model->getMeta($model::CSS_ROW_CLASS) : null; $modelId = ModelId::fromModel($model); - if ($environment->getClipboard()->hasId($modelId)) { + + if ( + null !== ($clipboard = $environment->getClipboard()) + && $clipboard->hasId($modelId) + ) { $cssClasses[] = 'tl_folder_clipped'; } @@ -516,7 +585,10 @@ private function addGroupHeader( */ private function panel(EnvironmentInterface $environment, $ignoredPanels = []) { - return (new PanelRenderer($environment->getView()))->render($ignoredPanels); + $view = $environment->getView(); + assert($view instanceof BackendViewInterface); + + return (new PanelRenderer($view))->render($ignoredPanels); } /** @@ -528,15 +600,17 @@ private function panel(EnvironmentInterface $environment, $ignoredPanels = []) */ private function getTableHead(EnvironmentInterface $environment) { - $tableHead = []; $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $tableHead = []; $properties = $definition->getPropertiesDefinition(); $formatter = $this->getViewSection($definition)->getListingConfig()->getLabelFormatter($definition->getName()); $sorting = ViewHelpers::getCurrentSorting($environment); $columns = $this->getSortingColumns($sorting); foreach ($formatter->getPropertyNames() as $field) { $tableHead[] = [ - 'class' => 'tl_folder_tlist col_' . $field . (in_array($field, $columns) ? ' ordered_by' : ''), + 'class' => 'tl_folder_tlist col_' . $field . (\in_array($field, $columns) ? ' ordered_by' : ''), 'content' => $properties->hasProperty($field) ? $properties->getProperty($field)->getLabel() : $this->translate($definition->getName() . '.' . $field . '.0', 'contao_' . $definition->getName()) @@ -560,7 +634,7 @@ private function getTableHead(EnvironmentInterface $environment) * @param int $groupLength The length of the value to use for grouping (only used when grouping. * @param EnvironmentInterface $environment The environment. * - * @return string + * @return string|null * * @SuppressWarnings(PHPMD.Superglobals) * @SuppressWarnings(PHPMD.CamelCaseVariableName) @@ -572,13 +646,19 @@ private function renderGroupHeader( $groupLength, EnvironmentInterface $environment ) { + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + // No property? Get out! - if (!$environment->getDataDefinition()->getPropertiesDefinition()->getProperty($field)) { + if (!$definition->getPropertiesDefinition()->hasProperty($field)) { return '-'; } + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + $event = new GetGroupHeaderEvent($environment, $model, $field, null, $groupMode, $groupLength); - $environment->getEventDispatcher()->dispatch($event, $event::NAME); + $dispatcher->dispatch($event, $event::NAME); return $event->getValue(); } @@ -594,7 +674,11 @@ protected function getSelectButtons(EnvironmentInterface $environment) { $event = new GetSelectModeButtonsEvent($environment); $event->setButtons([]); - $environment->getEventDispatcher()->dispatch($event, GetSelectModeButtonsEvent::NAME); + + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $dispatcher->dispatch($event, GetSelectModeButtonsEvent::NAME); return $event->getButtons(); } @@ -608,32 +692,54 @@ protected function getSelectButtons(EnvironmentInterface $environment) */ private function isSortable(EnvironmentInterface $environment) { + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + return ((true === (bool) ViewHelpers::getManualSortingProperty($environment)) - && (true === $environment->getDataDefinition()->getBasicDefinition()->isEditable())); + && (true === $definition->getBasicDefinition()->isEditable())); } /** * Render paste top button. Returns null if no button should be rendered. * - * @param EnvironmentInterface $environment The environment. - * @param GroupAndSortingDefinitionInterface|null $sorting The sorting mode. + * @param EnvironmentInterface $environment The + * env. + * @param GroupAndSortingDefinitionInterface|GroupAndSortingDefinitionCollectionInterface|null $sorting The + * sorting + * mode. * * @return string */ protected function renderPasteTopButton(EnvironmentInterface $environment, $sorting) { - $definition = $environment->getDataDefinition(); - $dispatcher = $environment->getEventDispatcher(); + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + $languageDomain = 'contao_' . $definition->getName(); $filter = new Filter(); - $filter->andModelIsFromProvider($definition->getBasicDefinition()->getDataProvider()); + assert($filter instanceof FilterInterface); - if (!$sorting || $environment->getClipboard()->isEmpty($filter)) { - return null; + $basicDefinition = $definition->getBasicDefinition(); + assert($basicDefinition instanceof BasicDefinitionInterface); + + $dataProvider = $basicDefinition->getDataProvider(); + assert(\is_string($dataProvider)); + + $filter->andModelIsFromProvider($dataProvider); + + $clipboard = $environment->getClipboard(); + assert($clipboard instanceof ClipboardInterface); + + if (!$sorting || $clipboard->isEmpty($filter)) { + return ''; } + if (!ViewHelpers::getManualSortingProperty($environment)) { - return null; + return ''; } /** @var AddToUrlEvent $urlEvent */ @@ -654,11 +760,11 @@ protected function renderPasteTopButton(EnvironmentInterface $environment, $sort ContaoEvents::IMAGE_GET_HTML ); - return sprintf( + return \sprintf( '%s', $urlEvent->getUrl(), StringUtil::specialchars($this->translate('pasteafter.0', $languageDomain)), - $imageEvent->getHtml() + $imageEvent->getHtml() ?? '' ); } @@ -675,7 +781,11 @@ protected function renderPasteTopButton(EnvironmentInterface $environment, $sort private function breadcrumb(EnvironmentInterface $environment) { $event = new GetBreadcrumbEvent($environment); - $environment->getEventDispatcher()->dispatch($event, $event::NAME); + + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $dispatcher->dispatch($event, $event::NAME); $elements = $event->getElements(); if (empty($elements)) { return null; @@ -723,10 +833,16 @@ private function getSortingColumns($sortingDefinition) */ private function getSelectContainer(EnvironmentInterface $environment) { - $inputProvider = $environment->getInputProvider(); + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + $sessionStorage = $environment->getSessionStorage(); + assert($sessionStorage instanceof SessionStorageInterface); - $sessionName = $environment->getDataDefinition()->getName() . '.' . $inputProvider->getParameter('mode'); + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $sessionName = $definition->getName() . '.' . $inputProvider->getParameter('mode'); if (!$sessionStorage->has($sessionName)) { return []; } @@ -759,7 +875,11 @@ private function handleEditAllButton(CollectionInterface $collection, Environmen } $dataDefinition = $environment->getDataDefinition(); - $backendView = $dataDefinition->getDefinition(Contao2BackendViewDefinitionInterface::NAME); + assert($dataDefinition instanceof ContainerInterface); + + $backendView = $dataDefinition->getDefinition(Contao2BackendViewDefinitionInterface::NAME); + assert($backendView instanceof Contao2BackendViewDefinitionInterface); + $globalCommands = $backendView->getGlobalCommands(); if (!$globalCommands->hasCommandNamed('all')) { diff --git a/src/Contao/View/Contao2BackendView/ActionHandler/CopyHandler.php b/src/Contao/View/Contao2BackendView/ActionHandler/CopyHandler.php index 6cec1b9d..4ff8475e 100644 --- a/src/Contao/View/Contao2BackendView/ActionHandler/CopyHandler.php +++ b/src/Contao/View/Contao2BackendView/ActionHandler/CopyHandler.php @@ -32,17 +32,22 @@ use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminatorAwareTrait; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Exception\NotCreatableException; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\ViewHelpers; +use ContaoCommunityAlliance\DcGeneral\Controller\ControllerInterface; +use ContaoCommunityAlliance\DcGeneral\Data\DataProviderInterface; use ContaoCommunityAlliance\DcGeneral\Data\ModelId; use ContaoCommunityAlliance\DcGeneral\Data\ModelIdInterface; use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; use ContaoCommunityAlliance\DcGeneral\Event\ActionEvent; use ContaoCommunityAlliance\DcGeneral\Event\PostDuplicateModelEvent; use ContaoCommunityAlliance\DcGeneral\Event\PreDuplicateModelEvent; +use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralRuntimeException; use ContaoCommunityAlliance\DcGeneral\View\ActionHandler\ActionGuardTrait; use ContaoCommunityAlliance\DcGeneral\View\ActionHandler\CallActionTrait; use ContaoCommunityAlliance\UrlBuilder\Contao\CsrfUrlBuilderFactory; use ContaoCommunityAlliance\UrlBuilder\UrlBuilder; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; /** * Class CopyModelController handles copy action on a model. @@ -89,7 +94,12 @@ public function handleEvent(ActionEvent $event) } $environment = $event->getEnvironment(); - if (!$environment->getDataDefinition()->getBasicDefinition()->isCreatable()) { + + if (null === ($definition = $environment->getDataDefinition())) { + return; + } + + if (!$definition->getBasicDefinition()->isCreatable()) { return; } @@ -98,7 +108,7 @@ public function handleEvent(ActionEvent $event) } if (true !== ($response = $this->checkPermission($environment))) { - $event->setResponse($response); + $event->setResponse((string) $response); $event->stopPropagation(); return; @@ -123,12 +133,15 @@ public function handleEvent(ActionEvent $event) protected function guardIsCreatable(EnvironmentInterface $environment, ModelIdInterface $modelId, $redirect = false) { $dataDefinition = $environment->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + if ($dataDefinition->getBasicDefinition()->isCreatable()) { return; } if ($redirect) { $eventDispatcher = $environment->getEventDispatcher(); + assert($eventDispatcher instanceof EventDispatcherInterface); $eventDispatcher->dispatch( new LogEvent( @@ -161,26 +174,44 @@ protected function guardIsCreatable(EnvironmentInterface $environment, ModelIdIn */ public function copy(EnvironmentInterface $environment, ModelIdInterface $modelId) { - $this->guardNotEditOnly($environment->getDataDefinition(), $modelId); + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $this->guardNotEditOnly($definition, $modelId); $this->guardIsCreatable($environment, $modelId); $dataProvider = $environment->getDataProvider(); - $model = $dataProvider->fetch($dataProvider->getEmptyConfig()->setId($modelId->getId())); + assert($dataProvider instanceof DataProviderInterface); + $model = $dataProvider->fetch($dataProvider->getEmptyConfig()->setId($modelId->getId())); + + if (!$model) { + throw new DcGeneralRuntimeException( + 'Model not found with ID ' . $modelId->getId() + ); + } + $controller = $environment->getController(); + assert($controller instanceof ControllerInterface); // We need to keep the original data here. - $copyModel = $environment->getController()->createClonedModel($model); + $copyModel = $controller->createClonedModel($model); - $eventDispatcher = $environment->getEventDispatcher(); - // Dispatch pre duplicate event. - $preCopyEvent = new PreDuplicateModelEvent($environment, $copyModel, $model); - $eventDispatcher->dispatch($preCopyEvent, $preCopyEvent::NAME); + $dispatcher = $environment->getEventDispatcher(); + if (null !== $dispatcher) { + // Dispatch pre duplicate event. + $preCopyEvent = new PreDuplicateModelEvent($environment, $copyModel, $model); + $dispatcher->dispatch($preCopyEvent, $preCopyEvent::NAME); + } // Save the copy. - $environment->getDataProvider($copyModel->getProviderName())->save($copyModel); - - // Dispatch post duplicate event. - $postCopyEvent = new PostDuplicateModelEvent($environment, $copyModel, $model); - $eventDispatcher->dispatch($postCopyEvent, $postCopyEvent::NAME); + $dataProvider = $environment->getDataProvider($copyModel->getProviderName()); + assert($dataProvider instanceof DataProviderInterface); + $dataProvider->save($copyModel); + + if (null !== $dispatcher) { + // Dispatch post duplicate event. + $postCopyEvent = new PostDuplicateModelEvent($environment, $copyModel, $model); + $dispatcher->dispatch($postCopyEvent, $postCopyEvent::NAME); + } return $copyModel; } @@ -195,18 +226,27 @@ public function copy(EnvironmentInterface $environment, ModelIdInterface $modelI */ protected function redirect($environment, $copiedModelId) { + if (null === ($inputProvider = $environment->getInputProvider())) { + return; + } + // Build a clean url to remove the copy related arguments instead of using the AddToUrlEvent. $urlBuilder = new UrlBuilder(); $urlBuilder ->setPath('contao') - ->setQueryParameter('do', $environment->getInputProvider()->getParameter('do')) + ->setQueryParameter('do', $inputProvider->getParameter('do')) ->setQueryParameter('table', $copiedModelId->getDataProviderName()) ->setQueryParameter('act', 'edit') ->setQueryParameter('id', $copiedModelId->getSerialized()) - ->setQueryParameter('pid', $environment->getInputProvider()->getParameter('pid')); + ->setQueryParameter('pid', $inputProvider->getParameter('pid')); $redirectEvent = new RedirectEvent($this->securityUrlBuilder->create($urlBuilder->getUrl())->getUrl()); - $environment->getEventDispatcher()->dispatch($redirectEvent, ContaoEvents::CONTROLLER_REDIRECT); + + if (null === ($dispatcher = $environment->getEventDispatcher())) { + return; + } + + $dispatcher->dispatch($redirectEvent, ContaoEvents::CONTROLLER_REDIRECT); } /** @@ -214,31 +254,43 @@ protected function redirect($environment, $copiedModelId) * * @param EnvironmentInterface $environment Current dc-general environment. * - * @return string|bool|null + * @return string|false|null */ protected function process(EnvironmentInterface $environment) { - $modelId = ModelId::fromSerialized($environment->getInputProvider()->getParameter('source')); + if (null === ($inputProvider = $environment->getInputProvider())) { + return false; + } - $dataDefinition = $environment->getDataDefinition(); - $this->guardValidEnvironment($dataDefinition, $modelId); + $modelId = ModelId::fromSerialized($inputProvider->getParameter('source')); + + if (null === ($definition = $environment->getDataDefinition())) { + return false; + } + + $this->guardValidEnvironment($definition, $modelId); // We want a redirect here if not creatable. $this->guardIsCreatable($environment, $modelId, true); - if ($dataDefinition->getBasicDefinition()->isEditOnlyMode()) { + if ($definition->getBasicDefinition()->isEditOnlyMode()) { return $this->callAction($environment, 'edit'); } // Manual sorting mode. The ClipboardController should pick it up. $manualSorting = ViewHelpers::getManualSortingProperty($environment); - if ($manualSorting && $environment->getDataProvider()->fieldExists($manualSorting)) { + + if (null === ($provider = $environment->getDataProvider())) { + return false; + } + + if ($manualSorting && $provider->fieldExists($manualSorting)) { return false; } $copiedModel = $this->copy($environment, $modelId); // If edit several don´t redirect do home. - if ('select' === $environment->getInputProvider()->getParameter('act')) { + if ('select' === $inputProvider->getParameter('act')) { return false; } @@ -256,15 +308,23 @@ protected function process(EnvironmentInterface $environment) */ private function checkPermission(EnvironmentInterface $environment) { - if (true === $environment->getDataDefinition()->getBasicDefinition()->isCreatable()) { + if (null === ($definition = $environment->getDataDefinition())) { + return false; + } + + if (true === $definition->getBasicDefinition()->isCreatable()) { return true; } + if (null === ($inputProvider = $environment->getInputProvider())) { + return ''; + } + return \sprintf( '
You have no permission for copy model %s.
', - ModelId::fromSerialized($environment->getInputProvider()->getParameter('source'))->getSerialized() + ModelId::fromSerialized($inputProvider->getParameter('source'))->getSerialized() ); } } diff --git a/src/Contao/View/Contao2BackendView/ActionHandler/CreateHandler.php b/src/Contao/View/Contao2BackendView/ActionHandler/CreateHandler.php index f5c07f22..fcbef4e8 100644 --- a/src/Contao/View/Contao2BackendView/ActionHandler/CreateHandler.php +++ b/src/Contao/View/Contao2BackendView/ActionHandler/CreateHandler.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2022 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -17,7 +17,7 @@ * @author Sven Baumann * @author Richard Henkenjohann * @author Ingolf Steinhardt - * @copyright 2013-2022 Contao Community Alliance. + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -30,10 +30,13 @@ use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\BaseView; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\EditMask; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\ViewHelpers; +use ContaoCommunityAlliance\DcGeneral\Data\DataProviderInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\BackCommand; use ContaoCommunityAlliance\DcGeneral\Data\DefaultEditInformation; use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; use ContaoCommunityAlliance\DcGeneral\Event\ActionEvent; +use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; /** * Class CreateHandler @@ -82,8 +85,9 @@ public function handleEvent(ActionEvent $event) $environment = $event->getEnvironment(); $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); - // Only handle if we do not have a manual sorting or we know where to insert. + // Only handle if we do not have a manual sorting, or we know where to insert. // Manual sorting is handled by clipboard. if ( ViewHelpers::getManualSortingProperty($environment) @@ -94,13 +98,13 @@ public function handleEvent(ActionEvent $event) } if (true !== ($response = $this->checkPermission($environment))) { - $event->setResponse($response); + $event->setResponse((string) $response); $event->stopPropagation(); return; } - if (false !== ($response = $this->process($environment))) { + if ('' !== ($response = $this->process($environment))) { $event->setResponse($response); } } @@ -115,9 +119,13 @@ public function handleEvent(ActionEvent $event) protected function process(EnvironmentInterface $environment) { $dataProvider = $environment->getDataProvider(); - $properties = $environment->getDataDefinition()->getPropertiesDefinition()->getProperties(); - $model = $dataProvider->getEmptyModel(); - $clone = $dataProvider->getEmptyModel(); + assert($dataProvider instanceof DataProviderInterface); + $dataDefinition = $environment->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + + $properties = $dataDefinition->getPropertiesDefinition()->getProperties(); + $model = $dataProvider->getEmptyModel(); + $clone = $dataProvider->getEmptyModel(); // If some of the fields have a default value, set it. foreach ($properties as $property) { @@ -133,10 +141,13 @@ protected function process(EnvironmentInterface $environment) $view = $environment->getView(); if (!$view instanceof BaseView) { - return false; + return ''; } - if ('select' !== $environment->getInputProvider()->getParameter('act')) { + $provider = $environment->getInputProvider(); + assert($provider instanceof InputProviderInterface); + + if ('select' !== $provider->getParameter('act')) { $this->handleGlobalCommands($environment); } @@ -154,6 +165,7 @@ protected function process(EnvironmentInterface $environment) private function checkPermission(EnvironmentInterface $environment) { $dataDefinition = $environment->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); if (true === $dataDefinition->getBasicDefinition()->isCreatable()) { return true; @@ -177,7 +189,11 @@ private function checkPermission(EnvironmentInterface $environment) protected function handleGlobalCommands(EnvironmentInterface $environment) { $dataDefinition = $environment->getDataDefinition(); - $backendView = $dataDefinition->getDefinition(Contao2BackendViewDefinitionInterface::NAME); + assert($dataDefinition instanceof ContainerInterface); + + $backendView = $dataDefinition->getDefinition(Contao2BackendViewDefinitionInterface::NAME); + assert($backendView instanceof Contao2BackendViewDefinitionInterface); + $globalCommands = $backendView->getGlobalCommands(); $globalCommands->clearCommands(); diff --git a/src/Contao/View/Contao2BackendView/ActionHandler/DeleteHandler.php b/src/Contao/View/Contao2BackendView/ActionHandler/DeleteHandler.php index f2e74dee..1e9c5698 100644 --- a/src/Contao/View/Contao2BackendView/ActionHandler/DeleteHandler.php +++ b/src/Contao/View/Contao2BackendView/ActionHandler/DeleteHandler.php @@ -31,10 +31,12 @@ use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Exception\EditOnlyModeException; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Exception\NotDeletableException; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\ViewHelpers; +use ContaoCommunityAlliance\DcGeneral\Data\DataProviderInterface; use ContaoCommunityAlliance\DcGeneral\Data\DefaultCollection; use ContaoCommunityAlliance\DcGeneral\Data\ModelId; use ContaoCommunityAlliance\DcGeneral\Data\ModelIdInterface; use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\DefaultModelRelationshipDefinition; use ContaoCommunityAlliance\DcGeneral\DataDefinition\ModelRelationship\ParentChildConditionInterface; use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; @@ -42,8 +44,10 @@ use ContaoCommunityAlliance\DcGeneral\Event\PostDeleteModelEvent; use ContaoCommunityAlliance\DcGeneral\Event\PreDeleteModelEvent; use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralRuntimeException; +use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; use ContaoCommunityAlliance\DcGeneral\View\ActionHandler\ActionGuardTrait; use ContaoCommunityAlliance\DcGeneral\View\ActionHandler\CallActionTrait; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; /** * Class DeleteHandler handles the delete action. @@ -84,7 +88,7 @@ public function handleEvent(ActionEvent $event) } if (true !== ($response = $this->checkPermission($event->getEnvironment()))) { - $event->setResponse($response); + $event->setResponse((string) $response); $event->stopPropagation(); return; @@ -108,6 +112,8 @@ public function handleEvent(ActionEvent $event) protected function guardIsDeletable(EnvironmentInterface $environment, ModelIdInterface $modelId, $redirect = false) { $dataDefinition = $environment->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + if ($dataDefinition->getBasicDefinition()->isDeletable()) { return; } @@ -117,6 +123,7 @@ protected function guardIsDeletable(EnvironmentInterface $environment, ModelIdIn } $eventDispatcher = $environment->getEventDispatcher(); + assert($eventDispatcher instanceof EventDispatcherInterface); $eventDispatcher->dispatch( new LogEvent( @@ -146,7 +153,9 @@ protected function guardIsDeletable(EnvironmentInterface $environment, ModelIdIn protected function fetchModel(EnvironmentInterface $environment, ModelIdInterface $modelId) { $dataProvider = $environment->getDataProvider($modelId->getDataProviderName()); - $model = $dataProvider->fetch($dataProvider->getEmptyConfig()->setId($modelId->getId())); + assert($dataProvider instanceof DataProviderInterface); + + $model = $dataProvider->fetch($dataProvider->getEmptyConfig()->setId($modelId->getId())); if (!$model || !$model->getId()) { throw new DcGeneralRuntimeException( @@ -171,21 +180,28 @@ protected function fetchModel(EnvironmentInterface $environment, ModelIdInterfac */ public function delete(EnvironmentInterface $environment, ModelIdInterface $modelId) { - $this->guardNotEditOnly($environment->getDataDefinition(), $modelId); + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $this->guardNotEditOnly($definition, $modelId); $this->guardIsDeletable($environment, $modelId); $model = $this->fetchModel($environment, $modelId); // Trigger event before the model will be deleted. $preDeleteEvent = new PreDeleteModelEvent($environment, $model); - $environment->getEventDispatcher()->dispatch($preDeleteEvent, $preDeleteEvent::NAME); + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $dispatcher->dispatch($preDeleteEvent, $preDeleteEvent::NAME); $dataProvider = $environment->getDataProvider($modelId->getDataProviderName()); + assert($dataProvider instanceof DataProviderInterface); $dataProvider->delete($model); // Trigger event after the model is deleted. $postDeleteEvent = new PostDeleteModelEvent($environment, $model); - $environment->getEventDispatcher()->dispatch($postDeleteEvent, $postDeleteEvent::NAME); + $dispatcher->dispatch($postDeleteEvent, $postDeleteEvent::NAME); } /** @@ -193,16 +209,19 @@ public function delete(EnvironmentInterface $environment, ModelIdInterface $mode * * @param EnvironmentInterface $environment The environment. * - * @return string + * @return string|null */ protected function process(EnvironmentInterface $environment) { - $inputProvider = $environment->getInputProvider(); + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + $dataDefinition = $environment->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); $modelId = ModelId::fromSerialized($inputProvider->getParameter('id')); - // Guard that we are in the preloaded environment. Otherwise checking the data definition could belong to + // Guard that we are in the preloaded environment. Otherwise, checking the data definition could belong to // another model. $this->guardValidEnvironment($dataDefinition, $modelId); @@ -234,15 +253,21 @@ protected function process(EnvironmentInterface $environment) */ private function checkPermission(EnvironmentInterface $environment) { - if (true === $environment->getDataDefinition()->getBasicDefinition()->isDeletable()) { + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + if (true === $definition->getBasicDefinition()->isDeletable()) { return true; } + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + return \sprintf( '
You have no permission for delete model %s.
', - ModelId::fromSerialized($environment->getInputProvider()->getParameter('id'))->getSerialized() + ModelId::fromSerialized($inputProvider->getParameter('id'))->getSerialized() ); } @@ -258,8 +283,11 @@ private function checkPermission(EnvironmentInterface $environment) */ protected function deepDelete(EnvironmentInterface $environment, ModelIdInterface $modelId) { + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + /** @var DefaultModelRelationshipDefinition $relationships */ - $relationships = $environment->getDataDefinition()->getDefinition('model-relationships'); + $relationships = $definition->getDefinition('model-relationships'); $childConditions = $relationships->getChildConditions($modelId->getDataProviderName()); @@ -271,11 +299,15 @@ protected function deepDelete(EnvironmentInterface $environment, ModelIdInterfac continue; } - $dataProvider = $environment->getDataProvider($modelId->getDataProviderName()); - $model = $dataProvider->fetch( - $dataProvider->getEmptyConfig()->setId($modelId->getId()) - ); + $dataProvider = $environment->getDataProvider($modelId->getDataProviderName()); + assert($dataProvider instanceof DataProviderInterface); + + $model = $dataProvider->fetch($dataProvider->getEmptyConfig()->setId($modelId->getId())); + assert($model instanceof ModelInterface); + $destinationChildDataProvider = $environment->getDataProvider($childCondition->getDestinationName()); + assert($destinationChildDataProvider instanceof DataProviderInterface); + /** @var DefaultCollection $destinationChildModels */ $destinationChildModels = $destinationChildDataProvider->fetchAll( @@ -291,9 +323,14 @@ protected function deepDelete(EnvironmentInterface $environment, ModelIdInterfac } foreach ($childConditions as $childCondition) { - $dataProvider = $environment->getDataProvider($modelId->getDataProviderName()); - $model = $dataProvider->fetch($dataProvider->getEmptyConfig()->setId($modelId->getId())); + $dataProvider = $environment->getDataProvider($modelId->getDataProviderName()); + assert($dataProvider instanceof DataProviderInterface); + + $model = $dataProvider->fetch($dataProvider->getEmptyConfig()->setId($modelId->getId())); + assert($model instanceof ModelInterface); + $childDataProvider = $environment->getDataProvider($childCondition->getDestinationName()); + assert($childDataProvider instanceof DataProviderInterface); $filters = $childCondition->getFilter($model); /** @var DefaultCollection $childModels */ @@ -303,15 +340,19 @@ protected function deepDelete(EnvironmentInterface $environment, ModelIdInterfac } foreach ($childModels as $childModel) { + if (null === ($dispatcher = $environment->getEventDispatcher())) { + continue; + } + // Trigger event before the model will be deleted. $preDeleteEvent = new PreDeleteModelEvent($environment, $childModel); - $environment->getEventDispatcher()->dispatch($preDeleteEvent, $preDeleteEvent::NAME); + $dispatcher->dispatch($preDeleteEvent, $preDeleteEvent::NAME); $childDataProvider->delete($childModel); // Trigger event after the model is deleted. $postDeleteEvent = new PostDeleteModelEvent($environment, $childModel); - $environment->getEventDispatcher()->dispatch($postDeleteEvent, $postDeleteEvent::NAME); + $dispatcher->dispatch($postDeleteEvent, $postDeleteEvent::NAME); } } } diff --git a/src/Contao/View/Contao2BackendView/ActionHandler/EditHandler.php b/src/Contao/View/Contao2BackendView/ActionHandler/EditHandler.php index 7751ae73..4d9f61a0 100644 --- a/src/Contao/View/Contao2BackendView/ActionHandler/EditHandler.php +++ b/src/Contao/View/Contao2BackendView/ActionHandler/EditHandler.php @@ -30,12 +30,17 @@ use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminatorAwareTrait; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\BaseView; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\EditMask; +use ContaoCommunityAlliance\DcGeneral\Data\DataProviderInterface; +use ContaoCommunityAlliance\DcGeneral\Data\ModelIdInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\BackCommand; use ContaoCommunityAlliance\DcGeneral\Data\DefaultEditInformation; use ContaoCommunityAlliance\DcGeneral\Data\ModelId; use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; use ContaoCommunityAlliance\DcGeneral\Event\ActionEvent; use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralRuntimeException; +use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; /** * Class CreateHandler @@ -100,15 +105,18 @@ public function handleEvent(ActionEvent $event) * * @param EnvironmentInterface $environment The environment. * - * @return string|bool + * @return string|false * * @throws DcGeneralRuntimeException When the requested model could not be located in the database. */ protected function process(EnvironmentInterface $environment) { $inputProvider = $environment->getInputProvider(); - $modelId = ModelId::fromSerialized($inputProvider->getParameter('id')); - $dataProvider = $environment->getDataProvider($modelId->getDataProviderName()); + assert($inputProvider instanceof InputProviderInterface); + + $modelId = ModelId::fromSerialized($inputProvider->getParameter('id')); + $dataProvider = $environment->getDataProvider($modelId->getDataProviderName()); + assert($dataProvider instanceof DataProviderInterface); $view = $environment->getView(); if (!$view instanceof BaseView) { @@ -143,11 +151,15 @@ private function checkPermission(ActionEvent $event) { $environment = $event->getEnvironment(); - if (true === $environment->getDataDefinition()->getBasicDefinition()->isEditable()) { + $dataDefinition = $environment->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + + if (true === $dataDefinition->getBasicDefinition()->isEditable()) { return true; } $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); $event->setResponse( \sprintf( @@ -168,7 +180,7 @@ private function checkPermission(ActionEvent $event) * reload of the page. * * @param EnvironmentInterface $environment The environment. - * @param ModelId $modelId The model id. + * @param ModelIdInterface $modelId The model id. * * @return void * @@ -176,12 +188,18 @@ private function checkPermission(ActionEvent $event) * * @SuppressWarnings(PHPMD.LongVariable) */ - private function checkRestoreVersion(EnvironmentInterface $environment, ModelId $modelId) + private function checkRestoreVersion(EnvironmentInterface $environment, ModelIdInterface $modelId) { $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + + $dataDefinition = $environment->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + + $dataProviderDefinition = $dataDefinition->getDataProviderDefinition(); - $dataProviderDefinition = $environment->getDataDefinition()->getDataProviderDefinition(); - $dataProvider = $environment->getDataProvider($modelId->getDataProviderName()); + $dataProvider = $environment->getDataProvider($modelId->getDataProviderName()); + assert($dataProvider instanceof DataProviderInterface); if ( !((null !== ($modelVersion = $inputProvider->getValue('version'))) @@ -191,6 +209,9 @@ private function checkRestoreVersion(EnvironmentInterface $environment, ModelId return; } + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + if (null === ($model = $dataProvider->getVersion($modelId->getId(), $modelVersion))) { $message = \sprintf( 'Could not load version %s of record ID %s from %s', @@ -199,7 +220,7 @@ private function checkRestoreVersion(EnvironmentInterface $environment, ModelId $modelId->getDataProviderName() ); - $environment->getEventDispatcher()->dispatch( + $dispatcher->dispatch( new LogEvent($message, 'ERROR', 'DC_General - checkRestoreVersion()'), ContaoEvents::SYSTEM_LOG ); @@ -209,7 +230,7 @@ private function checkRestoreVersion(EnvironmentInterface $environment, ModelId $dataProvider->save($model); $dataProvider->setVersionActive($modelId->getId(), $modelVersion); - $environment->getEventDispatcher()->dispatch(new ReloadEvent(), ContaoEvents::CONTROLLER_RELOAD); + $dispatcher->dispatch(new ReloadEvent(), ContaoEvents::CONTROLLER_RELOAD); } /** @@ -221,7 +242,12 @@ private function checkRestoreVersion(EnvironmentInterface $environment, ModelId */ protected function handleGlobalCommands(EnvironmentInterface $environment) { - $backendView = $environment->getDataDefinition()->getDefinition(Contao2BackendViewDefinitionInterface::NAME); + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $backendView = $definition->getDefinition(Contao2BackendViewDefinitionInterface::NAME); + assert($backendView instanceof Contao2BackendViewDefinitionInterface); + $globalCommands = $backendView->getGlobalCommands(); $globalCommands->clearCommands(); diff --git a/src/Contao/View/Contao2BackendView/ActionHandler/ListViewShowAllHandler.php b/src/Contao/View/Contao2BackendView/ActionHandler/ListViewShowAllHandler.php index 147c8608..8f1286e9 100644 --- a/src/Contao/View/Contao2BackendView/ActionHandler/ListViewShowAllHandler.php +++ b/src/Contao/View/Contao2BackendView/ActionHandler/ListViewShowAllHandler.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Sven Baumann * @author David Molineus - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -24,8 +25,10 @@ use ContaoCommunityAlliance\DcGeneral\Action; use ContaoCommunityAlliance\DcGeneral\Contao\DataDefinition\Definition\Contao2BackendViewDefinitionInterface; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\ContaoBackendViewTemplate; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\BasicDefinitionInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\GroupAndSortingInformationInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\ListingConfigInterface; use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; /** @@ -65,9 +68,16 @@ protected function determineTemplate($groupingInformation) */ protected function renderTemplate(ContaoBackendViewTemplate $template, EnvironmentInterface $environment) { - $dataDefinition = $environment->getDataDefinition(); - $viewDefinition = $dataDefinition->getDefinition(Contao2BackendViewDefinitionInterface::NAME); - $groupAndSorting = $viewDefinition->getListingConfig()->getGroupAndSortingDefinition(); + $dataDefinition = $environment->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + + $viewDefinition = $dataDefinition->getDefinition(Contao2BackendViewDefinitionInterface::NAME); + assert($viewDefinition instanceof Contao2BackendViewDefinitionInterface); + + $listingConfig = $viewDefinition->getListingConfig(); + assert($listingConfig instanceof ListingConfigInterface); + + $groupAndSorting = $listingConfig->getGroupAndSortingDefinition(); $pasteButton = $this->renderPasteTopButton($environment, $groupAndSorting); diff --git a/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/EditAllHandler.php b/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/EditAllHandler.php index 995c09c3..bad69dbf 100644 --- a/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/EditAllHandler.php +++ b/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/EditAllHandler.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,8 @@ * @package contao-community-alliance/dc-general * @author Sven Baumann * @author Richard Henkenjohann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0 * @filesource */ @@ -26,16 +27,20 @@ use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminatorAwareTrait; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\ContaoWidgetManager; use ContaoCommunityAlliance\DcGeneral\Data\DataProviderInterface; +use ContaoCommunityAlliance\DcGeneral\Data\EditInformationInterface; use ContaoCommunityAlliance\DcGeneral\Data\ModelId; use ContaoCommunityAlliance\DcGeneral\Data\ModelIdInterface; use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; use ContaoCommunityAlliance\DcGeneral\Data\PropertyValueBag; use ContaoCommunityAlliance\DcGeneral\Data\PropertyValueBagInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\Properties\PropertyInterface; use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; use ContaoCommunityAlliance\DcGeneral\Event\ActionEvent; +use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; use ContaoCommunityAlliance\DcGeneral\View\ActionHandler\AbstractPropertyOverrideEditAllHandler; use ContaoCommunityAlliance\DcGeneral\View\ActionHandler\CallActionTrait; +use ContaoCommunityAlliance\Translator\TranslatorInterface; /** * The class handle the "editAll" commands. @@ -60,7 +65,7 @@ public function __construct(RequestScopeDeterminator $scopeDeterminator) /** * {@inheritDoc} */ - public function handleEvent(ActionEvent $event) + public function handleEvent(ActionEvent $event): void { if ( !$this->getScopeDeterminator()->currentScopeIsBackend() @@ -69,19 +74,21 @@ public function handleEvent(ActionEvent $event) return; } - if (false !== ($response = $this->process($event->getAction(), $event->getEnvironment()))) { - $event->setResponse($response); - $event->stopPropagation(); - } + $response = $this->process($event->getAction(), $event->getEnvironment()); + $event->setResponse($response); + $event->stopPropagation(); } /** * {@inheritDoc} */ - private function process(Action $action, EnvironmentInterface $environment) + private function process(Action $action, EnvironmentInterface $environment): string { $inputProvider = $environment->getInputProvider(); - $translator = $environment->getTranslator(); + assert($inputProvider instanceof InputProviderInterface); + + $translator = $environment->getTranslator(); + assert($translator instanceof TranslatorInterface); $renderInformation = new \ArrayObject(); @@ -93,6 +100,9 @@ private function process(Action $action, EnvironmentInterface $environment) $this->handleSubmit($action, $environment); } + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + return $this->renderTemplate( $action, [ @@ -100,7 +110,7 @@ private function process(Action $action, EnvironmentInterface $environment) $translator->translate('MSC.' . $inputProvider->getParameter('mode') . 'Selected') . ': ' . $translator->translate('MSC.all.0'), 'fieldsets' => $renderInformation->offsetGet('fieldsets'), - 'table' => $environment->getDataDefinition()->getName(), + 'table' => $definition->getName(), 'error' => $renderInformation->offsetGet('error'), 'breadcrumb' => $this->renderBreadcrumb($environment), 'editButtons' => $this->getEditButtons($action, $environment), @@ -122,16 +132,22 @@ private function process(Action $action, EnvironmentInterface $environment) */ private function buildFieldSets(Action $action, \ArrayObject $renderInformation, EnvironmentInterface $environment) { - $formInputs = $environment->getInputProvider()->getValue('FORM_INPUTS'); + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + + $formInputs = $inputProvider->getValue('FORM_INPUTS'); $collection = $this->getCollectionFromSession($action, $environment); $fieldSets = []; $errors = []; while ($collection->count() > 0) { - $model = $collection->shift(); - $modelId = ModelId::fromModel($model); + if (null === ($model = $collection->shift())) { + continue; + } + $modelId = ModelId::fromModel($model); $propertyValuesBag = $this->getPropertyValueBagFromModel($action, $model, $environment); + if ($formInputs) { $this->handleEditCollection($action, $propertyValuesBag, $model, $renderInformation, $environment); } @@ -173,6 +189,8 @@ private function buildFieldSets(Action $action, \ArrayObject $renderInformation, private function handleLegendCollapsed(array $fieldSets) { $editInformation = System::getContainer()->get('cca.dc-general.edit-information'); + assert($editInformation instanceof EditInformationInterface); + if (!$editInformation->hasAnyModelError()) { return $fieldSets; } @@ -208,13 +226,20 @@ private function renderEditFields( PropertyValueBagInterface $propertyValuesBag, EnvironmentInterface $environment ) { - $properties = $environment->getDataDefinition()->getPropertiesDefinition(); + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $properties = $definition->getPropertiesDefinition(); - $selectProperties = (array) $this->getPropertiesFromSession($action, $environment); + $selectProperties = $this->getPropertiesFromSession($action, $environment); $modelId = ModelId::fromModel($model); $dataProvider = $environment->getDataProvider($modelId->getDataProviderName()); - $editModel = $dataProvider->fetch($dataProvider->getEmptyConfig()->setId($modelId->getId())); + assert($dataProvider instanceof DataProviderInterface); + + $editModel = $dataProvider->fetch($dataProvider->getEmptyConfig()->setId($modelId->getId())); + assert($editModel instanceof ModelInterface); + $visibleModel = $this->getVisibleModel($action, $editModel, $dataProvider, $environment); $fields = []; @@ -259,9 +284,12 @@ private function renderEditFields( } if (null === $fields[0]) { + $translator = $environment->getTranslator(); + assert($translator instanceof TranslatorInterface); + $fields[] = \sprintf( '

 

%s

 

', - $environment->getTranslator()->translate('MSC.no_properties_available') + $translator->translate('MSC.no_properties_available') ); } @@ -284,7 +312,7 @@ private function getVisibleModel( DataProviderInterface $dataProvider, EnvironmentInterface $environment ) { - $selectProperties = (array) $this->getPropertiesFromSession($action, $environment); + $selectProperties = $this->getPropertiesFromSession($action, $environment); $visibleModel = $dataProvider->getEmptyModel(); $visibleModel->setId($editModel->getId()); @@ -382,7 +410,9 @@ private function markModelErrors( EnvironmentInterface $environment ) { $editInformation = System::getContainer()->get('cca.dc-general.edit-information'); - $sessionValues = $this->getEditPropertiesByModelId($action, ModelId::fromModel($model), $environment); + assert($editInformation instanceof EditInformationInterface); + + $sessionValues = $this->getEditPropertiesByModelId($action, ModelId::fromModel($model), $environment); $modelError = $editInformation->getModelError($editModel); if ($modelError && isset($modelError[$selectProperty->getName()])) { @@ -421,7 +451,9 @@ private function handleEditCollection( \ArrayObject $renderInformation, EnvironmentInterface $environment ) { - $dataProvider = $environment->getDataProvider($model->getProviderName()); + $dataProvider = $environment->getDataProvider($model->getProviderName()); + assert($dataProvider instanceof DataProviderInterface); + $editCollection = $dataProvider->getEmptyCollection(); $revertCollection = $dataProvider->getEmptyCollection(); @@ -458,7 +490,7 @@ private function buildEditProperty(PropertyInterface $originalProperty, ModelIdI $editProperty->setSearchable($originalProperty->isSearchable()); $editProperty->setFilterable($originalProperty->isFilterable()); $editProperty->setWidgetType($originalProperty->getWidgetType()); - $editProperty->setOptions($originalProperty->getOptions()); + $editProperty->setOptions($originalProperty->getOptions() ?? []); $editProperty->setExplanation($originalProperty->getExplanation()); $editProperty->setExtra($originalProperty->getExtra()); diff --git a/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/OverrideAllHandler.php b/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/OverrideAllHandler.php index c438ae8d..b70cced3 100644 --- a/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/OverrideAllHandler.php +++ b/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/OverrideAllHandler.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2021 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,8 @@ * @package contao-community-alliance/dc-general * @author Sven Baumann * @author Richard Henkenjohann - * @copyright 2013-2021 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0 * @filesource */ @@ -23,20 +24,29 @@ use Contao\System; use Contao\Widget; use ContaoCommunityAlliance\DcGeneral\Action; +use ContaoCommunityAlliance\DcGeneral\Contao\Compatibility\DcCompat; use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminator; use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminatorAwareTrait; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\ContaoWidgetManager; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\EncodePropertyValueFromWidgetEvent; +use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Widget\AbstractWidget; +use ContaoCommunityAlliance\DcGeneral\Data\EditInformationInterface; use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; use ContaoCommunityAlliance\DcGeneral\Data\PropertyValueBag; use ContaoCommunityAlliance\DcGeneral\Data\PropertyValueBagInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; use ContaoCommunityAlliance\DcGeneral\Event\ActionEvent; +use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; use ContaoCommunityAlliance\DcGeneral\View\ActionHandler\AbstractPropertyOverrideEditAllHandler; use ContaoCommunityAlliance\DcGeneral\View\ActionHandler\CallActionTrait; +use ContaoCommunityAlliance\Translator\TranslatorInterface; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; /** * The class handle the "overrideAll" commands. + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class OverrideAllHandler extends AbstractPropertyOverrideEditAllHandler { @@ -56,7 +66,7 @@ public function __construct(RequestScopeDeterminator $scopeDeterminator) /** * {@inheritDoc} */ - public function handleEvent(ActionEvent $event) + public function handleEvent(ActionEvent $event): void { if ( !$this->getScopeDeterminator()->currentScopeIsBackend() @@ -65,7 +75,7 @@ public function handleEvent(ActionEvent $event) return; } - if (false !== ($response = $this->process($event->getAction(), $event->getEnvironment()))) { + if ('' !== ($response = $this->process($event->getAction(), $event->getEnvironment()))) { $event->setResponse($response); $event->stopPropagation(); } @@ -83,9 +93,14 @@ public function handleEvent(ActionEvent $event) */ private function process(Action $action, EnvironmentInterface $environment) { - $inputProvider = $environment->getInputProvider(); - $translator = $environment->getTranslator(); + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + + $translator = $environment->getTranslator(); + assert($translator instanceof TranslatorInterface); + $editInformation = System::getContainer()->get('cca.dc-general.edit-information'); + assert($editInformation instanceof EditInformationInterface); $renderInformation = new \ArrayObject(); @@ -109,6 +124,9 @@ private function process(Action $action, EnvironmentInterface $environment) $this->handleSubmit($action, $environment); } + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + return $this->renderTemplate( $action, [ @@ -116,11 +134,11 @@ private function process(Action $action, EnvironmentInterface $environment) $translator->translate('MSC.' . $inputProvider->getParameter('mode') . 'Selected') . ': ' . $translator->translate('MSC.all.0'), 'fieldsets' => $renderInformation->offsetGet('fieldsets'), - 'table' => $environment->getDataDefinition()->getName(), + 'table' => $definition->getName(), 'error' => $renderInformation->offsetGet('error'), 'breadcrumb' => $this->renderBreadcrumb($environment), 'editButtons' => $this->getEditButtons($action, $environment), - 'noReload' => (bool) $editInformation->hasAnyModelError() + 'noReload' => $editInformation->hasAnyModelError() ] ); } @@ -152,6 +170,7 @@ protected function handleInvalidPropertyValueBag( } $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); foreach (\array_keys($propertyValueBag->getArrayCopy()) as $propertyName) { $allErrors = $propertyValueBag->getPropertyValueErrors($propertyName); @@ -172,7 +191,11 @@ protected function handleInvalidPropertyValueBag( $event = new EncodePropertyValueFromWidgetEvent($environment, $model, $eventPropertyValueBag); $event->setProperty($propertyName) ->setValue($inputProvider->getValue($propertyName, true)); - $environment->getEventDispatcher()->dispatch($event, EncodePropertyValueFromWidgetEvent::NAME); + + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $dispatcher->dispatch($event, EncodePropertyValueFromWidgetEvent::NAME); $propertyValueBag->setPropertyValue($propertyName, $event->getValue()); @@ -239,17 +262,17 @@ private function getOverrideProperties(Action $action, EnvironmentInterface $env /** * Render the field sets. * - * @param Action $action The action. - * @param \ArrayObject $renderInformation The render information. - * @param PropertyValueBagInterface|null $propertyValues The property values. - * @param EnvironmentInterface $environment The environment. + * @param Action $action The action. + * @param \ArrayObject $renderInformation The render information. + * @param PropertyValueBagInterface $propertyValues The property values. + * @param EnvironmentInterface $environment The environment. * * @return void */ private function renderFieldSets( Action $action, \ArrayObject $renderInformation, - PropertyValueBagInterface $propertyValues = null, + PropertyValueBagInterface $propertyValues, EnvironmentInterface $environment ) { $properties = $this->getOverrideProperties($action, $environment); @@ -260,7 +283,7 @@ private function renderFieldSets( $errors = []; $fieldSet = ['palette' => '', 'class' => 'tl_box']; - $propertyNames = $propertyValues ? \array_keys($propertyValues->getArrayCopy()) : \array_keys($properties); + $propertyNames = \array_keys($propertyValues->getArrayCopy()); foreach ($propertyNames as $propertyName) { $errors = $this->getPropertyValueErrors($propertyValues, $propertyName, $errors); @@ -274,12 +297,13 @@ private function renderFieldSets( $this->setDefaultValue($model, $propertyValues, $propertyName, $environment); $widget = $widgetManager->getWidget($property->getName(), $propertyValues); + assert($widget instanceof Widget); $widgetModel = $this->getModelFromWidget($widget); if (!$this->ensurePropertyVisibleInModel($action, $property->getName(), $widgetModel, $environment)) { $fieldSet['palette'] .= - $this->injectSelectParentPropertyInformation($action, $property, $widgetModel, $environment); + $this->injectSelectParentPropertyInformation($action, $property, $widgetModel, $environment) ?? ''; continue; } @@ -299,13 +323,16 @@ private function renderFieldSets( $widgetModel, $propertyValues, $environment - ); + ) ?? ''; } + $translator = $environment->getTranslator(); + assert($translator instanceof TranslatorInterface); + if (empty($fieldSet['palette'])) { $fieldSet['palette'] = \sprintf( '

 

%s

 

', - $environment->getTranslator()->translate('MSC.no_properties_available') + $translator->translate('MSC.no_properties_available') ); } @@ -320,13 +347,16 @@ private function renderFieldSets( * * @return ModelInterface */ - private function getModelFromWidget(Widget $widget) + private function getModelFromWidget(Widget $widget): ModelInterface { - if ($widget->dataContainer) { + if ($widget->dataContainer instanceof DcCompat) { return $widget->dataContainer->getModel(); } + if ($widget instanceof AbstractWidget) { + return $widget->getModel(); + } - return $widget->getModel(); + throw new \InvalidArgumentException('Expected an instance of ' . AbstractWidget::class); } /** @@ -338,11 +368,13 @@ private function getModelFromWidget(Widget $widget) * * @return array */ - private function getPropertyValueErrors(PropertyValueBagInterface $propertyValueBag, $propertyName, array $errors) - { + private function getPropertyValueErrors( + PropertyValueBagInterface $propertyValueBag, + string $propertyName, + array $errors + ): array { if ( - (null !== $propertyValueBag) - && $propertyValueBag->hasPropertyValue($propertyName) + $propertyValueBag->hasPropertyValue($propertyName) && $propertyValueBag->isPropertyValueInvalid($propertyName) ) { $errors = \array_merge( @@ -367,10 +399,13 @@ private function getPropertyValueErrors(PropertyValueBagInterface $propertyValue private function setDefaultValue( ModelInterface $model, PropertyValueBagInterface $propertyValueBag, - $propertyName, + string $propertyName, EnvironmentInterface $environment - ) { - $propertiesDefinition = $environment->getDataDefinition()->getPropertiesDefinition(); + ): void { + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $propertiesDefinition = $definition->getPropertiesDefinition(); // If in the intersect model the value available, then set it as default. if ($modelValue = $model->getProperty($propertyName)) { @@ -381,7 +416,8 @@ private function setDefaultValue( if ( $propertiesDefinition->hasProperty($propertyName) - && !$environment->getInputProvider()->hasValue($propertyName) + && null !== ($inputProvider = $environment->getInputProvider()) + && !$inputProvider->hasValue($propertyName) ) { $propertyValueBag->setPropertyValue( $propertyName, diff --git a/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/PasteAllHandler.php b/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/PasteAllHandler.php index 3b34d451..ef26626b 100644 --- a/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/PasteAllHandler.php +++ b/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/PasteAllHandler.php @@ -20,24 +20,33 @@ namespace ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\ActionHandler\MultipleHandler; +use ContaoCommunityAlliance\DcGeneral\Clipboard\ClipboardInterface; use ContaoCommunityAlliance\DcGeneral\Clipboard\Filter; use ContaoCommunityAlliance\DcGeneral\Clipboard\ItemInterface; use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminator; use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminatorAwareTrait; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\ViewHelpers; use ContaoCommunityAlliance\DcGeneral\Data\CollectionInterface; +use ContaoCommunityAlliance\DcGeneral\Data\DataProviderInterface; use ContaoCommunityAlliance\DcGeneral\Data\ModelId; use ContaoCommunityAlliance\DcGeneral\Data\ModelIdInterface; +use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\BasicDefinitionInterface; use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; use ContaoCommunityAlliance\DcGeneral\Event\ActionEvent; use ContaoCommunityAlliance\DcGeneral\Event\PostDuplicateModelEvent; use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralInvalidArgumentException; +use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; use ContaoCommunityAlliance\DcGeneral\View\ActionHandler\CallActionTrait; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; /** * Action handler for paste all action. * * @return void + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class PasteAllHandler { @@ -47,16 +56,16 @@ class PasteAllHandler /** * The copied model is available by paste mode copy. * - * @var ModelIdInterface + * @var ModelInterface|null */ - protected $copiedModel; + protected $copiedModel = null; /** * The original model is available by paste mode copy. * - * @var ModelIdInterface + * @var ModelInterface|null */ - protected $originalModel; + protected $originalModel = null; /** @@ -72,7 +81,7 @@ public function __construct(RequestScopeDeterminator $scopeDeterminator) /** * {@inheritDoc} */ - public function handleEvent(ActionEvent $event) + public function handleEvent(ActionEvent $event): void { if ( !$this->getScopeDeterminator()->currentScopeIsBackend() @@ -101,7 +110,10 @@ private function process(EnvironmentInterface $environment) } $inputProvider = $environment->getInputProvider(); - $clipboard = $environment->getClipboard(); + assert($inputProvider instanceof InputProviderInterface); + + $clipboard = $environment->getClipboard(); + assert($clipboard instanceof ClipboardInterface); $inputProvider->setParameter('pasteAll', true); @@ -131,17 +143,30 @@ private function process(EnvironmentInterface $environment) */ protected function getClipboardItems(EnvironmentInterface $environment) { - $basicDefinition = $environment->getDataDefinition()->getBasicDefinition(); + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $basicDefinition = $definition->getBasicDefinition(); + assert($basicDefinition instanceof BasicDefinitionInterface); + + $provider = $basicDefinition->getDataProvider(); + assert(\is_string($provider)); + + $parentProvider = $basicDefinition->getParentDataProvider(); + assert(\is_string($parentProvider)); $filter = new Filter(); - $filter->andModelIsFromProvider($basicDefinition->getDataProvider()); + $filter->andModelIsFromProvider($provider); if ($basicDefinition->getParentDataProvider()) { - $filter->andParentIsFromProvider($basicDefinition->getParentDataProvider()); + $filter->andParentIsFromProvider($parentProvider); } else { $filter->andHasNoParent(); } - return $environment->getClipboard()->fetch($filter); + $clipboard = $environment->getClipboard(); + assert($clipboard instanceof ClipboardInterface); + + return $clipboard->fetch($filter); } /** @@ -154,6 +179,8 @@ protected function getClipboardItems(EnvironmentInterface $environment) protected function getCollection(EnvironmentInterface $environment) { $dataDefinition = $environment->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + $relationShip = $dataDefinition->getModelRelationshipDefinition(); if (!$relationShip->getChildCondition($dataDefinition->getName(), $dataDefinition->getName())) { @@ -173,6 +200,7 @@ protected function getCollection(EnvironmentInterface $environment) protected function getFlatCollection(EnvironmentInterface $environment) { $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); $previousItem = null; $collection = []; @@ -207,9 +235,15 @@ protected function getFlatCollection(EnvironmentInterface $environment) */ protected function getHierarchyCollection(array $clipboardItems, EnvironmentInterface $environment) { - $dataProvider = $environment->getDataProvider(); + $dataProvider = $environment->getDataProvider(); + assert($dataProvider instanceof DataProviderInterface); + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + $dataDefinition = $environment->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + $relationShip = $dataDefinition->getModelRelationshipDefinition(); $childCondition = $relationShip->getChildCondition($dataDefinition->getName(), $dataDefinition->getName()); if (null === $childCondition) { @@ -241,19 +275,19 @@ protected function getHierarchyCollection(array $clipboardItems, EnvironmentInte $previousItem = $clipboardItem; - $model = - $dataProvider->fetch($dataProvider->getEmptyConfig()->setId($modelId->getId())); + $model = $dataProvider->fetch($dataProvider->getEmptyConfig()->setId($modelId->getId())); + assert($model instanceof ModelInterface); $itemCollection = $dataProvider->fetchAll($dataProvider->getEmptyConfig()->setFilter($childCondition->getFilter($model))); - if ($itemCollection) { - $collection = $this->setSubItemsToCollection( - $clipboardItem, - $this->getSubClipboardItems($clipboardItems, $itemCollection), - $collection, - $environment - ); - } + assert($itemCollection instanceof CollectionInterface); + + $collection = $this->setSubItemsToCollection( + $clipboardItem, + $this->getSubClipboardItems($clipboardItems, $itemCollection), + $collection, + $environment + ); } return $collection; @@ -307,7 +341,11 @@ protected function setSubItemsToCollection( } $dataProvider = $environment->getDataProvider(); + assert($dataProvider instanceof DataProviderInterface); + $dataDefinition = $environment->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + $relationShip = $dataDefinition->getModelRelationshipDefinition(); $childCondition = $relationShip->getChildCondition($dataDefinition->getName(), $dataDefinition->getName()); if (null === $childCondition) { @@ -338,19 +376,19 @@ protected function setSubItemsToCollection( 'pasteMode' => $intoItem ? 'after' : 'into' ]; - $model = - $dataProvider->fetch($dataProvider->getEmptyConfig()->setId($modelId->getId())); + $model = $dataProvider->fetch($dataProvider->getEmptyConfig()->setId($modelId->getId())); + assert($model instanceof ModelInterface); + $itemCollection = $dataProvider->fetchAll($dataProvider->getEmptyConfig()->setFilter($childCondition->getFilter($model))); + assert($itemCollection instanceof CollectionInterface); - if ($itemCollection) { - $collection = $this->setSubItemsToCollection( - $subClipboardItem, - $this->getSubClipboardItems($this->getClipboardItems($environment), $itemCollection), - $collection, - $environment - ); - } + $collection = $this->setSubItemsToCollection( + $subClipboardItem, + $this->getSubClipboardItems($this->getClipboardItems($environment), $itemCollection), + $collection, + $environment + ); } return $collection; @@ -365,7 +403,10 @@ protected function setSubItemsToCollection( */ protected function addDispatchDuplicateModel(EnvironmentInterface $environment) { - $environment->getEventDispatcher()->addListener( + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $dispatcher->addListener( PostDuplicateModelEvent::NAME, function (PostDuplicateModelEvent $event) { $this->copiedModel = $event->getModel(); @@ -385,6 +426,8 @@ function (PostDuplicateModelEvent $event) { protected function setParameterForPaste(array $collectionItem, EnvironmentInterface $environment) { $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + $clipboardItem = $collectionItem['item']; $inputProvider->unsetParameter('after'); @@ -405,6 +448,7 @@ protected function setParameterForPaste(array $collectionItem, EnvironmentInterf return; } + assert($this->copiedModel instanceof ModelInterface); $copiedModelId = ModelId::fromModel($this->copiedModel); $inputProvider->setParameter($collectionItem['pasteMode'], $copiedModelId->getSerialized()); diff --git a/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/SelectModelAllHandler.php b/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/SelectModelAllHandler.php index 56b22af9..92d5a52d 100644 --- a/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/SelectModelAllHandler.php +++ b/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/SelectModelAllHandler.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -12,7 +12,7 @@ * * @package contao-community-alliance/dc-general * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0 * @filesource */ @@ -24,6 +24,7 @@ use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminatorAwareTrait; use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; use ContaoCommunityAlliance\DcGeneral\Event\ActionEvent; +use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; use ContaoCommunityAlliance\DcGeneral\View\ActionHandler\CallActionTrait; /** @@ -60,7 +61,7 @@ public function handleEvent(ActionEvent $event) return; } - if (false !== ($response = $this->process($event->getAction(), $event->getEnvironment()))) { + if ('' !== ($response = $this->process($event->getAction(), $event->getEnvironment()))) { $event->setResponse($response); } } @@ -76,6 +77,7 @@ public function handleEvent(ActionEvent $event) private function process(Action $action, EnvironmentInterface $environment) { $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); return $this->callAction( $environment, @@ -87,6 +89,6 @@ private function process(Action $action, EnvironmentInterface $environment) 'select' => $inputProvider->getParameter('select') ] ) - ); + ) ?? ''; } } diff --git a/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/SelectPropertyAllHandler.php b/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/SelectPropertyAllHandler.php index acbdfcc6..6d10da9c 100644 --- a/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/SelectPropertyAllHandler.php +++ b/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/SelectPropertyAllHandler.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2022 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,7 @@ * @package contao-community-alliance/dc-general * @author Sven Baumann * @author Ingolf Steinhardt - * @copyright 2013-2022 Contao Community Alliance. + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0 * @filesource */ @@ -31,6 +31,7 @@ use ContaoCommunityAlliance\DcGeneral\Data\DataProviderInterface; use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; use ContaoCommunityAlliance\DcGeneral\Data\NoOpDataProvider; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\BasicDefinitionInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\Properties\DefaultProperty; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\Properties\PropertyInterface; @@ -38,6 +39,9 @@ use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; use ContaoCommunityAlliance\DcGeneral\Event\ActionEvent; use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralRuntimeException; +use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; +use ContaoCommunityAlliance\DcGeneral\SessionStorageInterface; +use ContaoCommunityAlliance\Translator\TranslatorInterface; /** * This class handles the rendering of list view "showAllProperties" actions. @@ -64,11 +68,10 @@ public function handleEvent(ActionEvent $event) !$this->getScopeDeterminator()->currentScopeIsBackend() || ('selectPropertyAll' !== $event->getAction()->getName()) ) { - return null; + return; } - $response = $this->process($event->getAction(), $event->getEnvironment()); - if (false !== $response) { + if (null !== $response = $this->process($event->getAction(), $event->getEnvironment())) { $event->setResponse($response); } } @@ -79,10 +82,13 @@ public function handleEvent(ActionEvent $event) protected function process(Action $action, EnvironmentInterface $environment) { $dataDefinition = $environment->getDataDefinition(); - $backendView = $dataDefinition->getDefinition(Contao2BackendViewDefinitionInterface::NAME); + assert($dataDefinition instanceof ContainerInterface); + + $backendView = $dataDefinition->getDefinition(Contao2BackendViewDefinitionInterface::NAME); + assert($backendView instanceof Contao2BackendViewDefinitionInterface); $backendView->getListingConfig()->setShowColumns(false); - $environment->getDataDefinition()->getBasicDefinition()->setMode(BasicDefinitionInterface::MODE_FLAT); + $dataDefinition->getBasicDefinition()->setMode(BasicDefinitionInterface::MODE_FLAT); return parent::process($action, $environment); } @@ -112,7 +118,10 @@ protected function loadCollection(EnvironmentInterface $environment) */ private function getPropertyDataProvider(EnvironmentInterface $environment) { - $providerName = 'property.' . $environment->getDataDefinition()->getName(); + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $providerName = 'property.' . $definition->getName(); $dataProvider = new NoOpDataProvider(); $dataProvider->setBaseConfig(['name' => $providerName]); @@ -132,13 +141,19 @@ private function getPropertyDataProvider(EnvironmentInterface $environment) */ private function setPropertyLabelFormatter($providerName, EnvironmentInterface $environment) { - $properties = $environment->getDataDefinition()->getPropertiesDefinition(); + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $properties = $definition->getPropertiesDefinition(); $labelFormatter = (new DefaultModelFormatterConfig()) ->setPropertyNames(['name', 'description']) ->setFormat('%s [%s]'); - $this->getViewSection($environment->getDataDefinition()) + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $this->getViewSection($definition) ->getListingConfig() ->setLabelFormatter($providerName, $labelFormatter); @@ -154,7 +169,7 @@ private function setPropertyLabelFormatter($providerName, EnvironmentInterface $ } /** - * Return the field collection for each properties. + * Return the field collection for each property. * * @param DataProviderInterface $dataProvider The field data provider. * @param EnvironmentInterface $environment The environment. @@ -165,7 +180,10 @@ private function getCollection(DataProviderInterface $dataProvider, EnvironmentI { $collection = $dataProvider->getEmptyCollection(); - foreach ($environment->getDataDefinition()->getPropertiesDefinition() as $property) { + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + foreach ($definition->getPropertiesDefinition() as $property) { if (!$this->isPropertyAllowed($property, $environment)) { continue; } @@ -205,17 +223,21 @@ private function isPropertyAllowed(PropertyInterface $property, EnvironmentInter } $translator = $environment->getTranslator(); + assert($translator instanceof TranslatorInterface); - $extra = (array) $property->getExtra(); + $extra = $property->getExtra(); if ( $this->isPropertyAllowedByEdit($extra, $environment) || $this->isPropertyAllowedByOverride($extra, $environment) ) { + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + Message::addInfo( \sprintf( $translator->translate('MSC.not_allowed_property_info'), $property->getLabel() ?: $property->getName(), - $translator->translate('MSC.' . $environment->getInputProvider()->getParameter('mode') . 'Selected') + $translator->translate('MSC.' . $inputProvider->getParameter('mode') . 'Selected') ) ); @@ -235,8 +257,11 @@ private function isPropertyAllowed(PropertyInterface $property, EnvironmentInter */ private function isPropertyAllowedByEdit(array $extra, EnvironmentInterface $environment) { + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + return (true === ($extra['doNotEditMultiple'] ?? false)) - && ('edit' === $environment->getInputProvider()->getParameter('mode')); + && ('edit' === $inputProvider->getParameter('mode')); } /** @@ -249,10 +274,13 @@ private function isPropertyAllowedByEdit(array $extra, EnvironmentInterface $env */ private function isPropertyAllowedByOverride(array $extra, EnvironmentInterface $environment) { + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + return ((true === ($extra['unique'] ?? false)) || isset($extra['readonly']) || (true === ($extra['doNotOverrideMultiple'] ?? false))) - && ('override' === $environment->getInputProvider()->getParameter('mode')); + && ('override' === $inputProvider->getParameter('mode')); } /** @@ -268,8 +296,13 @@ private function isPropertyAllowedByIntersectProperties( EnvironmentInterface $environment ) { $sessionStorage = $environment->getSessionStorage(); + assert($sessionStorage instanceof SessionStorageInterface); + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + $dataDefinition = $environment->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); $session = $sessionStorage->get($dataDefinition->getName() . '.' . $inputProvider->getParameter('mode')); @@ -341,8 +374,11 @@ private function handlePropertyFileTreeOrder(PropertyInterface $property, ModelI */ protected function renderTemplate(ContaoBackendViewTemplate $template, EnvironmentInterface $environment) { - $inputProvider = $environment->getInputProvider(); + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + $dataDefinition = $environment->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); $languageDomain = 'contao_' . $dataDefinition->getName(); @@ -388,7 +424,10 @@ protected function renderTemplate(ContaoBackendViewTemplate $template, Environme */ protected function getSelectButtons(EnvironmentInterface $environment) { - $languageDomain = 'contao_' . $environment->getDataDefinition()->getName(); + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $languageDomain = 'contao_' . $definition->getName(); $confirmMessage = \htmlentities( \sprintf( @@ -403,29 +442,34 @@ protected function getSelectButtons(EnvironmentInterface $environment) 'BackendGeneral.hideMessage(); return false;' ) ); - $onClick = 'BackendGeneral.confirmSelectOverrideEditAll(this, \'properties[]\', \'' . - $confirmMessage . '\'); return false;'; - - $continueName = $environment->getInputProvider()->getParameter('mode'); - $buttons['continue'] = \sprintf( - '', - $continueName, - $continueName, - 'c', - StringUtil::specialchars($this->translate('MSC.continue', $languageDomain)), - $onClick - ); - return $buttons; + $onClick = 'BackendGeneral.confirmSelectOverrideEditAll(this, \'properties[]\', \'' . + $confirmMessage . '\'); return false;'; + + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + + $continueName = $inputProvider->getParameter('mode'); + + return [ + 'continue' => \sprintf( + '', + $continueName, + $continueName, + 'c', + StringUtil::specialchars($this->translate('MSC.continue', $languageDomain)), + $onClick + ) + ]; } /** * Check if the action should be handled. * - * @param string $mode The list mode. + * @param int $mode The list mode. * @param Action $action The action. * - * @return mixed + * @return bool */ protected function wantToHandle($mode, Action $action) { diff --git a/src/Contao/View/Contao2BackendView/ActionHandler/ParentedListViewShowAllHandler.php b/src/Contao/View/Contao2BackendView/ActionHandler/ParentedListViewShowAllHandler.php index 6cfd4091..5a7f00b7 100644 --- a/src/Contao/View/Contao2BackendView/ActionHandler/ParentedListViewShowAllHandler.php +++ b/src/Contao/View/Contao2BackendView/ActionHandler/ParentedListViewShowAllHandler.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2022 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -16,7 +16,7 @@ * @author David Molineus * @author Richard Henkenjohann * @author Ingolf Steinhardt - * @copyright 2013-2022 Contao Community Alliance. + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -31,13 +31,18 @@ use ContaoCommunityAlliance\Contao\Bindings\Events\Date\ParseDateEvent; use ContaoCommunityAlliance\Contao\Bindings\Events\Image\GenerateHtmlEvent; use ContaoCommunityAlliance\DcGeneral\Action; +use ContaoCommunityAlliance\DcGeneral\BaseConfigRegistryInterface; +use ContaoCommunityAlliance\DcGeneral\Clipboard\ClipboardInterface; use ContaoCommunityAlliance\DcGeneral\Clipboard\Filter; use ContaoCommunityAlliance\DcGeneral\Clipboard\ItemInterface; use ContaoCommunityAlliance\DcGeneral\Contao\DataDefinition\Definition\Contao2BackendViewDefinitionInterface; +use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\BackendViewInterface; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\ContaoBackendViewTemplate; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\GetParentHeaderEvent; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\ParentViewChildRecordEvent; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\ViewHelpers; +use ContaoCommunityAlliance\DcGeneral\Data\CollectionInterface; +use ContaoCommunityAlliance\DcGeneral\Data\DataProviderInterface; use ContaoCommunityAlliance\DcGeneral\Data\ModelId; use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; @@ -47,6 +52,10 @@ use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\GroupAndSortingInformationInterface; use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralRuntimeException; +use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; +use ContaoCommunityAlliance\DcGeneral\Panel\PanelContainerInterface; +use ContaoCommunityAlliance\DcGeneral\View\ViewInterface; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; /** * This class handles the rendering of parented list view "showAll" actions. @@ -72,19 +81,20 @@ protected function wantToHandle($mode, Action $action) protected function renderModel(ModelInterface $model, EnvironmentInterface $environment) { $event = new ParentViewChildRecordEvent($environment, $model); - $environment->getEventDispatcher()->dispatch($event, $event::NAME); - - if (null !== $event->getHtml()) { - $information = [ - [ - 'colspan' => 1, - 'class' => 'tl_file_list col_1', - 'content' => $event->getHtml() - ] - ]; - $model->setMeta($model::LABEL_VALUE, $information); - return; - } + + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $dispatcher->dispatch($event, $event::NAME); + + $information = [ + [ + 'colspan' => 1, + 'class' => 'tl_file_list col_1', + 'content' => $event->getHtml() + ] + ]; + $model->setMeta($model::LABEL_VALUE, $information); parent::renderModel($model, $environment); } @@ -129,7 +139,10 @@ protected function renderTemplate(ContaoBackendViewTemplate $template, Environme */ protected function loadParentModel(EnvironmentInterface $environment) { - $pidDetails = ModelId::fromSerialized($environment->getInputProvider()->getParameter('pid')); + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + + $pidDetails = ModelId::fromSerialized($inputProvider->getParameter('pid')); if (!($provider = $environment->getDataProvider($pidDetails->getDataProviderName()))) { throw new DcGeneralRuntimeException( @@ -160,9 +173,17 @@ protected function loadParentModel(EnvironmentInterface $environment) private function renderHeaderFields($parentModel, EnvironmentInterface $environment) { $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + $parentName = $definition->getBasicDefinition()->getParentDataProvider(); - $add = []; - $properties = $environment->getParentDataDefinition()->getPropertiesDefinition(); + assert(\is_string($parentName)); + + $add = []; + + $parentDefinition = $environment->getParentDataDefinition(); + assert($parentDefinition instanceof ContainerInterface); + + $properties = $parentDefinition->getPropertiesDefinition(); foreach ($this->getViewSection($definition)->getListingConfig()->getHeaderPropertyNames() as $field) { $value = StringUtil::deserialize($parentModel->getProperty($field)); @@ -181,9 +202,12 @@ private function renderHeaderFields($parentModel, EnvironmentInterface $environm $event = new GetParentHeaderEvent($environment, $parentModel); $event->setAdditional($add); - $environment->getEventDispatcher()->dispatch($event, GetParentHeaderEvent::NAME); + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $dispatcher->dispatch($event, GetParentHeaderEvent::NAME); - if (null !== !$event->getAdditional()) { + if ($event->getAdditional()) { $add = $event->getAdditional(); } @@ -204,7 +228,7 @@ function ($value) { * @param string $field The field name. * @param string $parentName The parent definition name. * - * @return array|string + * @return string */ private function translateHeaderColumnName($field, $parentName) { @@ -239,11 +263,11 @@ private function renderParentProperty(EnvironmentInterface $environment, $proper : $value; $options = $property->getOptions(); - if ((isset($evaluation['isAssociative']) && $evaluation['isAssociative']) || ArrayUtil::isAssoc($options)) { + if (\is_array($options) && (($evaluation['isAssociative'] ?? false) || ArrayUtil::isAssoc($options))) { $value = $options[$value]; } - return $value; + return $value ?? ''; } /** @@ -304,7 +328,11 @@ private function renderForDateTime( $isRendered = true; $event = new ParseDateEvent($value, Config::get($evaluation['rgxp'] . 'Format')); - $environment->getEventDispatcher()->dispatch($event, ContaoEvents::DATE_PARSE); + + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $dispatcher->dispatch($event, ContaoEvents::DATE_PARSE); return $event->getResult(); } @@ -342,12 +370,25 @@ private function renderReference($value, $reference, &$isRendered) */ private function getParentModelButtons($parentModel, EnvironmentInterface $environment) { - if ('select' === $environment->getInputProvider()->getParameter('act')) { + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + + if ('select' === $inputProvider->getParameter('act')) { return ''; } - $config = $environment->getBaseConfigRegistry()->getBaseConfig(); - $environment->getView()->getPanel()->initialize($config); + $registry = $environment->getBaseConfigRegistry(); + assert($registry instanceof BaseConfigRegistryInterface); + + $view = $environment->getView(); + assert($view instanceof BackendViewInterface); + + $panel = $view->getPanel(); + assert($panel instanceof PanelContainerInterface); + + $config = $registry->getBaseConfig(); + + $panel->initialize($config); if (!$config->getSorting()) { return ''; } @@ -373,23 +414,33 @@ private function getParentModelButtons($parentModel, EnvironmentInterface $envir protected function getHeaderEditButton(ModelInterface $parentModel, EnvironmentInterface $environment) { $parentDefinition = $environment->getParentDataDefinition(); + assert($parentDefinition instanceof ContainerInterface); + + $backendView = $parentDefinition->getDefinition(Contao2BackendViewDefinitionInterface::NAME); + assert($backendView instanceof Contao2BackendViewDefinitionInterface); + /** @var CommandCollectionInterface $commands */ - $commands = $parentDefinition - ->getDefinition(Contao2BackendViewDefinitionInterface::NAME) - ->getModelCommands(); + $commands = $backendView->getModelCommands(); if (!$commands->hasCommandNamed('edit') || !$parentDefinition->getBasicDefinition()->isEditable()) { return null; } - $parentName = $environment->getDataDefinition()->getBasicDefinition()->getParentDataProvider(); + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $parentName = $definition->getBasicDefinition()->getParentDataProvider(); $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); $command = $commands->getCommandNamed('edit'); $parameters = (array) $command->getParameters(); // This should be set in command builder rather than here. - $parameters['do'] = $environment->getInputProvider()->getParameter('do'); + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + + $parameters['do'] = $inputProvider->getParameter('do'); $parameters['table'] = $parentName; $parameters['pid'] = ''; @@ -418,7 +469,7 @@ protected function getHeaderEditButton(ModelInterface $parentModel, EnvironmentI $href = ''; foreach ($parameters as $key => $value) { - $href .= \sprintf('&%s=%s', $key, $value); + $href .= \sprintf('&%s=%s', $key, $value ?? ''); } /** @var AddToUrlEvent $urlAfter */ $urlAfter = $dispatcher->dispatch(new AddToUrlEvent($href), ContaoEvents::BACKEND_ADD_TO_URL); @@ -429,7 +480,7 @@ protected function getHeaderEditButton(ModelInterface $parentModel, EnvironmentI StringUtil::specialchars( \sprintf($this->translate('editheader.1', $parentDefinition->getName()), $parentModel->getId()) ), - $imageEvent->getHtml() + $imageEvent->getHtml() ?? '' ); } @@ -443,23 +494,36 @@ protected function getHeaderEditButton(ModelInterface $parentModel, EnvironmentI */ private function getHeaderPasteNewButton(ModelInterface $parentModel, EnvironmentInterface $environment) { - $basicDefinition = $environment->getDataDefinition()->getBasicDefinition(); + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $basicDefinition = $definition->getBasicDefinition(); + assert($basicDefinition instanceof BasicDefinitionInterface); + if (!$basicDefinition->isCreatable()) { return null; } + $dataProvider = $basicDefinition->getDataProvider(); + assert(\is_string($dataProvider)); + $filter = new Filter(); - $filter->andModelIsFromProvider($basicDefinition->getDataProvider()); + $filter->andModelIsFromProvider($dataProvider); if ($parentProviderName = $basicDefinition->getParentDataProvider()) { $filter->andParentIsFromProvider($parentProviderName); } else { $filter->andHasNoParent(); } - if ($environment->getClipboard()->isNotEmpty($filter)) { + $clipboard = $environment->getClipboard(); + assert($clipboard instanceof ClipboardInterface); + + if ($clipboard->isNotEmpty($filter)) { return null; } + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); /** @var AddToUrlEvent $urlEvent */ $urlEvent = $dispatcher->dispatch( @@ -468,6 +532,8 @@ private function getHeaderPasteNewButton(ModelInterface $parentModel, Environmen ); $parentDefinition = $environment->getParentDataDefinition(); + assert($parentDefinition instanceof ContainerInterface); + /** @var GenerateHtmlEvent $imageEvent */ $imageEvent = $dispatcher->dispatch( new GenerateHtmlEvent( @@ -481,7 +547,7 @@ private function getHeaderPasteNewButton(ModelInterface $parentModel, Environmen '%s', $urlEvent->getUrl(), StringUtil::specialchars($this->translate('pastenew.0', $parentDefinition->getName())), - $imageEvent->getHtml() + $imageEvent->getHtml() ?? '' ); } @@ -495,14 +561,25 @@ private function getHeaderPasteNewButton(ModelInterface $parentModel, Environmen */ private function getHeaderPasteTopButton(ModelInterface $parentModel, EnvironmentInterface $environment) { - $definition = $environment->getDataDefinition(); + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + $basicDefinition = $definition->getBasicDefinition(); + assert($basicDefinition instanceof BasicDefinitionInterface); + + $dataProvider = $basicDefinition->getDataProvider(); + assert(\is_string($dataProvider)); + + $dataParentProvider = $basicDefinition->getParentDataProvider(); + assert(\is_string($dataParentProvider)); $filter = new Filter(); - $filter->andModelIsFromProvider($basicDefinition->getDataProvider()); - $filter->andParentIsFromProvider($basicDefinition->getParentDataProvider()); + $filter->andModelIsFromProvider($dataProvider); + $filter->andParentIsFromProvider($dataParentProvider); $clipboard = $environment->getClipboard(); + assert($clipboard instanceof ClipboardInterface); + if ($clipboard->isEmpty($filter)) { return null; } @@ -513,22 +590,33 @@ private function getHeaderPasteTopButton(ModelInterface $parentModel, Environmen $subFilter->andParentIsNot(ModelId::fromModel($parentModel)); $subFilter->orActionIsIn([ItemInterface::COPY, ItemInterface::DEEP_COPY]); + $dataProvider = $basicDefinition->getDataProvider(); + assert(\is_string($dataProvider)); + + $dataParentProvider = $basicDefinition->getParentDataProvider(); + assert(is_string($dataParentProvider)); + $filter = new Filter(); - $filter->andModelIsFromProvider($basicDefinition->getDataProvider()); - $filter->andParentIsFromProvider($basicDefinition->getParentDataProvider()); + $filter->andModelIsFromProvider($dataProvider); + $filter->andParentIsFromProvider($dataParentProvider); $filter->andSub($subFilter); $allowPasteTop = (bool) $clipboard->fetch($filter); } $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $dataProvider = $basicDefinition->getDataProvider(); + assert(\is_string($dataProvider)); + if ($allowPasteTop) { /** @var AddToUrlEvent $urlEvent */ $urlEvent = $dispatcher->dispatch( new AddToUrlEvent( 'act=paste' . '&pid=' . ModelId::fromModel($parentModel)->getSerialized() . - '&after=' . ModelId::fromValues($basicDefinition->getDataProvider(), '0')->getSerialized() + '&after=' . ModelId::fromValues($dataProvider, '0')->getSerialized() ), ContaoEvents::BACKEND_ADD_TO_URL ); @@ -547,7 +635,7 @@ private function getHeaderPasteTopButton(ModelInterface $parentModel, Environmen '%s', $urlEvent->getUrl(), StringUtil::specialchars($this->translate('pasteafter.0', $definition->getName())), - $imageEvent->getHtml() + $imageEvent->getHtml() ?? '' ); } @@ -565,7 +653,7 @@ private function getHeaderPasteTopButton(ModelInterface $parentModel, Environmen } /** - * Obtain the id of the grand parent (if any). + * Obtain the id of the grand-parent (if any). * * @param ContainerInterface $parentDefinition The parent definition. * @param ModelInterface $parentModel The parent model. @@ -578,11 +666,14 @@ private function getGrandParentId( ModelInterface $parentModel, EnvironmentInterface $environment ) { - if ('' === ($grandParentName = $parentDefinition->getBasicDefinition()->getParentDataProvider())) { + if (null === ($grandParentName = $parentDefinition->getBasicDefinition()->getParentDataProvider())) { return null; } - $relationship = $environment->getDataDefinition()->getModelRelationshipDefinition()->getChildCondition( + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $relationship = $definition->getModelRelationshipDefinition()->getChildCondition( $grandParentName, $parentDefinition->getName() ); @@ -592,15 +683,21 @@ private function getGrandParentId( } $grandParentProvider = $environment->getDataProvider($grandParentName); + assert($grandParentProvider instanceof DataProviderInterface); $config = $grandParentProvider->getEmptyConfig(); $config->setFilter((array) $relationship->getInverseFilterFor($parentModel)); $parents = $grandParentProvider->fetchAll($config); + assert($parents instanceof CollectionInterface); + + $firstModel = $parents->get(0); + assert($firstModel instanceof ModelInterface); if (1 === $parents->length()) { - return ModelId::fromModel($parents->get(0))->getSerialized(); + return ModelId::fromModel($firstModel)->getSerialized(); } + return false; } } diff --git a/src/Contao/View/Contao2BackendView/ActionHandler/PasteHandler.php b/src/Contao/View/Contao2BackendView/ActionHandler/PasteHandler.php index a94d2c23..62141f14 100644 --- a/src/Contao/View/Contao2BackendView/ActionHandler/PasteHandler.php +++ b/src/Contao/View/Contao2BackendView/ActionHandler/PasteHandler.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Sven Baumann * @author David Molineus - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -26,7 +27,10 @@ use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminator; use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminatorAwareTrait; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\ViewHelpers; +use ContaoCommunityAlliance\DcGeneral\Controller\ControllerInterface; use ContaoCommunityAlliance\DcGeneral\Data\ModelId; +use ContaoCommunityAlliance\DcGeneral\Data\ModelIdInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\BasicDefinitionInterface; use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; use ContaoCommunityAlliance\DcGeneral\Event\ActionEvent; @@ -74,7 +78,7 @@ public function handleEvent(ActionEvent $event) return; } - if (false !== ($response = $this->process($event->getEnvironment()))) { + if (null !== ($response = $this->process($event->getEnvironment()))) { $event->setResponse($response); } } @@ -88,20 +92,31 @@ public function handleEvent(ActionEvent $event) */ protected function process(EnvironmentInterface $environment) { - $input = $environment->getInputProvider(); - $clipboard = $environment->getClipboard(); - $definition = $environment->getDataDefinition()->getBasicDefinition(); + $input = $environment->getInputProvider(); + assert($input instanceof InputProviderInterface); + + $clipboard = $environment->getClipboard(); + assert($clipboard instanceof ClipboardInterface); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $basicDefinition = $definition->getBasicDefinition(); + assert($basicDefinition instanceof BasicDefinitionInterface); // Tree mode needs special handling. - if ($this->needTreeModeShowAll($definition, $input)) { + if ($this->needTreeModeShowAll($basicDefinition, $input)) { return $this->callAction($environment, 'showAll'); } + $providerName = $basicDefinition->getDataProvider(); + assert(\is_string($providerName)); + // Check if it is a simple create-paste of a single model, if so, redirect to edit view. if ( $this->isSimpleCreatePaste( $clipboard, - $environment->getDataDefinition()->getBasicDefinition()->getDataProvider() + $providerName ) ) { return $this->callAction($environment, 'create'); @@ -113,11 +128,15 @@ protected function process(EnvironmentInterface $environment) $parentModelId = $this->modelIdFromParameter($input, 'pid'); $items = []; - $environment->getController()->applyClipboardActions($source, $after, $into, $parentModelId, null, $items); + $controller = $environment->getController(); + assert($controller instanceof ControllerInterface); + + $controller->applyClipboardActions($source, $after, $into, $parentModelId, null, $items); foreach ($items as $item) { $clipboard->remove($item); } + $clipboard->saveTo($environment); // If we use paste all handler don´t redirect yet. @@ -139,7 +158,10 @@ protected function process(EnvironmentInterface $environment) */ private function checkPermission(ActionEvent $event) { - if (true === $event->getEnvironment()->getDataDefinition()->getBasicDefinition()->isEditable()) { + $definition = $event->getEnvironment()->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + if (true === $definition->getBasicDefinition()->isEditable()) { return true; } @@ -200,7 +222,7 @@ private function isSimpleCreatePaste(ClipboardInterface $clipboard, $provider) * @param InputProviderInterface $input The input provider. * @param string $name The parameter to retrieve. * - * @return ModelId|null + * @return ModelIdInterface|null */ private function modelIdFromParameter(InputProviderInterface $input, $name) { diff --git a/src/Contao/View/Contao2BackendView/ActionHandler/SelectHandler.php b/src/Contao/View/Contao2BackendView/ActionHandler/SelectHandler.php index 27de31fa..79635779 100644 --- a/src/Contao/View/Contao2BackendView/ActionHandler/SelectHandler.php +++ b/src/Contao/View/Contao2BackendView/ActionHandler/SelectHandler.php @@ -23,9 +23,11 @@ namespace ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\ActionHandler; +use ArrayObject; use ContaoCommunityAlliance\Contao\Bindings\ContaoEvents; use ContaoCommunityAlliance\Contao\Bindings\Events\System\GetReferrerEvent; use ContaoCommunityAlliance\DcGeneral\Action; +use ContaoCommunityAlliance\DcGeneral\Clipboard\ClipboardInterface; use ContaoCommunityAlliance\DcGeneral\Clipboard\Filter; use ContaoCommunityAlliance\DcGeneral\Contao\DataDefinition\Definition\Contao2BackendViewDefinitionInterface; use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminator; @@ -33,14 +35,38 @@ use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\PrepareMultipleModelsActionEvent; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\ViewHelpers; use ContaoCommunityAlliance\DcGeneral\Data\CollectionInterface; +use ContaoCommunityAlliance\DcGeneral\Data\DataProviderInterface; use ContaoCommunityAlliance\DcGeneral\Data\ModelId; +use ContaoCommunityAlliance\DcGeneral\Data\ModelIdInterface; use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\BasicDefinitionInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\Command; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\CommandCollectionInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\PaletteInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\PropertyInterface; use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; use ContaoCommunityAlliance\DcGeneral\Event\ActionEvent; +use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; +use ContaoCommunityAlliance\DcGeneral\SessionStorageInterface; use ContaoCommunityAlliance\DcGeneral\View\ActionHandler\CallActionTrait; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; + +use function array_filter; +use function array_intersect; +use function array_intersect_key; +use function array_map; +use function array_unique; +use function assert; +use function count; +use function in_array; +use function is_array; +use function is_string; +use function method_exists; +use function serialize; +use function sprintf; +use function ucfirst; +use function unserialize; /** * Class SelectController. @@ -84,12 +110,12 @@ public function handleEvent(ActionEvent $event) return; } - if (false !== ($response = $this->process($action, $event->getEnvironment()))) { + if (null !== ($response = $this->process($action, $event->getEnvironment()))) { $event->setResponse($response); // Stop the event here. // Don´t allow any listener for manipulation here. - // Use the sub events their are called. + // Use the sub events there are called. $event->stopPropagation(); } } @@ -100,16 +126,16 @@ public function handleEvent(ActionEvent $event) * @param Action $action The action. * @param EnvironmentInterface $environment The environment. * - * @return string + * @return string|null */ private function process(Action $action, EnvironmentInterface $environment) { - $actionMethod = \sprintf( + $actionMethod = sprintf( 'handle%sAllAction', - \ucfirst($this->getSubmitAction($environment, $this->regardSelectMode($environment))) + ucfirst($this->getSubmitAction($environment, $this->regardSelectMode($environment))) ); - if (false !== ($response = $this->{$actionMethod}($environment, $action))) { + if (null !== ($response = $this->{$actionMethod}($environment, $action))) { return $response; } @@ -126,12 +152,15 @@ private function process(Action $action, EnvironmentInterface $environment) */ private function getSubmitAction(EnvironmentInterface $environment, $regardSelectMode = false) { + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + if ( !$regardSelectMode - && $environment->getInputProvider()->hasParameter('select') - && !$environment->getInputProvider()->hasValue('properties') + && $inputProvider->hasParameter('select') + && !$inputProvider->hasValue('properties') ) { - return 'select' . \ucfirst($environment->getInputProvider()->getParameter('select')); + return 'select' . ucfirst($inputProvider->getParameter('select')); } if (null !== ($action = $this->determineAction($environment))) { @@ -139,11 +168,11 @@ private function getSubmitAction(EnvironmentInterface $environment, $regardSelec } if ($regardSelectMode) { - return $environment->getInputProvider()->getParameter('mode') ?: null; + return $inputProvider->getParameter('mode') ?: ''; } - return $environment->getInputProvider()->getParameter('select') ? - 'select' . \ucfirst($environment->getInputProvider()->getParameter('select')) : null; + return $inputProvider->getParameter('select') ? + 'select' . ucfirst($inputProvider->getParameter('select')) : ''; } /** @@ -155,13 +184,16 @@ private function getSubmitAction(EnvironmentInterface $environment, $regardSelec */ private function determineAction(EnvironmentInterface $environment) { + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + foreach (['delete', 'cut', 'copy', 'override', 'edit'] as $action) { if ( - $environment->getInputProvider()->hasValue($action) - || $environment->getInputProvider()->hasValue($action . '_save') - || $environment->getInputProvider()->hasValue($action . '_saveNback') + $inputProvider->hasValue($action) + || $inputProvider->hasValue($action . '_save') + || $inputProvider->hasValue($action . '_saveNback') ) { - $environment->getInputProvider()->setParameter('mode', $action); + $inputProvider->setParameter('mode', $action); return $action; } @@ -180,9 +212,10 @@ private function determineAction(EnvironmentInterface $environment) private function regardSelectMode(EnvironmentInterface $environment) { $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); $regardSelectMode = false; - \array_map( + array_map( function ($value) use ($inputProvider, &$regardSelectMode) { if (!$inputProvider->hasValue($value)) { return false; @@ -211,28 +244,31 @@ function ($value) use ($inputProvider, &$regardSelectMode) { * @param Action $action The dcg action. * @param string $submitAction The submit action name. * - * @return ModelId[] + * @return list */ - private function getModelIds(EnvironmentInterface $environment, Action $action, $submitAction) + private function getModelIds(EnvironmentInterface $environment, Action $action, string $submitAction): array { - $valueKey = \in_array($submitAction, ['edit', 'override']) ? 'properties' : 'models'; - $modelIds = (array) $environment->getInputProvider()->getValue($valueKey); + $valueKey = in_array($submitAction, ['edit', 'override']) ? 'properties' : 'models'; - if (!empty($modelIds)) { - $modelIds = \array_map( - function ($modelId) { - return ModelId::fromSerialized($modelId); - }, - $modelIds - ); + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); - $event = new PrepareMultipleModelsActionEvent($environment, $action, $modelIds, $submitAction); - $environment->getEventDispatcher()->dispatch($event, $event::NAME); + $modelIds = array_values((array) $inputProvider->getValue($valueKey)); - $modelIds = $event->getModelIds(); + if (empty($modelIds)) { + return []; } + $modelIds = array_map( + static fn (string $modelId): ModelIdInterface => ModelId::fromSerialized($modelId), + $modelIds + ); + + $event = new PrepareMultipleModelsActionEvent($environment, $action, $modelIds, $submitAction); + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + $dispatcher->dispatch($event, $event::NAME); - return $modelIds; + return $event->getModelIds(); } /** @@ -251,11 +287,7 @@ private function handleSelectModelsAllAction(EnvironmentInterface $environment, $this->clearClipboard($environment); $this->handleGlobalCommands($environment); - if ($response = $this->callAction($environment, 'selectModelAll')) { - return $response; - } - - return null; + return $this->callAction($environment, 'selectModelAll'); } /** @@ -282,11 +314,7 @@ private function handleSelectPropertiesAllAction(EnvironmentInterface $environme $this->setIntersectProperties($collection, $environment); $this->setIntersectValues($collection, $environment); - if ($response = $this->callAction($environment, 'selectPropertyAll')) { - return $response; - } - - return null; + return $this->callAction($environment, 'selectPropertyAll'); } /** @@ -305,6 +333,8 @@ private function handleDeleteAllAction(EnvironmentInterface $environment, Action $this->handleGlobalCommands($environment); $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + foreach ($this->getModelIds($environment, $action, $this->getSubmitAction($environment)) as $modelId) { $inputProvider->setParameter('id', $modelId->getSerialized()); @@ -331,6 +361,8 @@ private function handleDeleteAllAction(EnvironmentInterface $environment, Action private function handleCutAllAction(EnvironmentInterface $environment, Action $action) { $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + foreach ($this->getModelIds($environment, $action, $this->getSubmitAction($environment)) as $modelId) { $inputProvider->setParameter('source', $modelId->getSerialized()); @@ -357,6 +389,8 @@ private function handleCutAllAction(EnvironmentInterface $environment, Action $a private function handleCopyAllAction(EnvironmentInterface $environment, Action $action) { $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + foreach ($this->getModelIds($environment, $action, $this->getSubmitAction($environment)) as $modelId) { $inputProvider->setParameter('source', $modelId->getSerialized()); @@ -376,7 +410,7 @@ private function handleCopyAllAction(EnvironmentInterface $environment, Action $ * @param EnvironmentInterface $environment The environment. * @param Action $action The action. * - * @return string + * @return string|null * * @SuppressWarnings(PHPMD.UnusedFormalParameter) * @SuppressWarnings(PHPMD.UnusedPrivateMethod) @@ -400,7 +434,7 @@ private function handleEditAllAction(EnvironmentInterface $environment, Action $ * @param EnvironmentInterface $environment The environment. * @param Action $action The action. * - * @return string + * @return string|null * * @SuppressWarnings(PHPMD.UnusedFormalParameter) * @SuppressWarnings(PHPMD.UnusedPrivateMethod) @@ -428,11 +462,17 @@ private function handleOverrideAllAction(EnvironmentInterface $environment, Acti private function handleGlobalCommands(EnvironmentInterface $environment) { $dataDefinition = $environment->getDataDefinition(); - $backendView = $dataDefinition->getDefinition(Contao2BackendViewDefinitionInterface::NAME); + assert($dataDefinition instanceof ContainerInterface); + + $backendView = $dataDefinition->getDefinition(Contao2BackendViewDefinitionInterface::NAME); + assert($backendView instanceof Contao2BackendViewDefinitionInterface); + + $globalCommands = $backendView->getGlobalCommands(); + assert($globalCommands instanceof CommandCollectionInterface); $backButton = null; - if ($backendView->getGlobalCommands()->hasCommandNamed('back_button')) { - $backButton = $backendView->getGlobalCommands()->getCommandNamed('back_button'); + if ($globalCommands->hasCommandNamed('back_button')) { + $backButton = $globalCommands->getCommandNamed('back_button'); } if (!$backButton) { @@ -441,7 +481,7 @@ private function handleGlobalCommands(EnvironmentInterface $environment) $parametersBackButton = $backButton->getParameters(); - if (\in_array($this->getSelectAction($environment), ['properties', 'edit'])) { + if (in_array($this->getSelectAction($environment), ['properties', 'edit'])) { $parametersBackButton->offsetSet('act', 'select'); $parametersBackButton->offsetSet( 'select', @@ -464,8 +504,8 @@ private function handleGlobalCommands(EnvironmentInterface $environment) ->setName('close_all_button') ->setLabel('MSC.closeAll.0') ->setDescription('MSC.closeAll.1') - ->setParameters(new \ArrayObject()) - ->setExtra(new \ArrayObject($closeExtra)) + ->setParameters(new ArrayObject()) + ->setExtra(new ArrayObject($closeExtra)) ->setDisabled(false); } @@ -478,7 +518,10 @@ private function handleGlobalCommands(EnvironmentInterface $environment) */ private function getSelectAction(EnvironmentInterface $environment) { - return $environment->getInputProvider()->getParameter('select'); + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + + return $inputProvider->getParameter('select'); } /** @@ -490,14 +533,21 @@ private function getSelectAction(EnvironmentInterface $environment) */ private function getReferrerUrl(EnvironmentInterface $environment) { + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $parentDefinition = $environment->getParentDataDefinition(); $event = new GetReferrerEvent( true, - (null !== $environment->getParentDataDefinition()) - ? $environment->getParentDataDefinition()->getName() - : $environment->getDataDefinition()->getName() + (null !== $parentDefinition) + ? $parentDefinition->getName() + : $definition->getName() ); - $environment->getEventDispatcher()->dispatch($event, ContaoEvents::SYSTEM_GET_REFERRER); + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $dispatcher->dispatch($event, ContaoEvents::SYSTEM_GET_REFERRER); return $event->getReferrerUrl(); } @@ -511,20 +561,29 @@ private function getReferrerUrl(EnvironmentInterface $environment) */ private function clearClipboard(EnvironmentInterface $environment) { - $basicDefinition = $environment->getDataDefinition()->getBasicDefinition(); + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $basicDefinition = $definition->getBasicDefinition(); + assert($basicDefinition instanceof BasicDefinitionInterface); $filter = new Filter(); - $filter->andModelIsFromProvider($basicDefinition->getDataProvider()); - if ($basicDefinition->getParentDataProvider()) { - $filter->andParentIsFromProvider($basicDefinition->getParentDataProvider()); + + $dataProvider = $basicDefinition->getDataProvider(); + assert(is_string($dataProvider)); + + $filter->andModelIsFromProvider($dataProvider); + if (null !== ($parentProvider = $basicDefinition->getParentDataProvider())) { + $filter->andParentIsFromProvider($parentProvider); } else { $filter->andHasNoParent(); } $clipboard = $environment->getClipboard(); + assert($clipboard instanceof ClipboardInterface); $items = $clipboard->fetch($filter); - if (\count($items) < 1) { + if (count($items) < 1) { return; } @@ -542,9 +601,15 @@ private function clearClipboard(EnvironmentInterface $environment) */ private function getSelectCollection(EnvironmentInterface $environment) { - $sessionStorage = $environment->getSessionStorage(); $dataDefinition = $environment->getDataDefinition(); - $dataProvider = $environment->getDataProvider(); + assert($dataDefinition instanceof ContainerInterface); + + $sessionStorage = $environment->getSessionStorage(); + assert($sessionStorage instanceof SessionStorageInterface); + + $dataProvider = $environment->getDataProvider(); + assert($dataProvider instanceof DataProviderInterface); + $session = $sessionStorage->get($dataDefinition->getName() . '.' . $this->getSubmitAction($environment, true)); @@ -553,8 +618,8 @@ private function getSelectCollection(EnvironmentInterface $environment) $modelIds[] = ModelId::fromSerialized($modelId)->getId(); } - $idProperty = \method_exists($dataProvider, 'getIdProperty') ? $dataProvider->getIdProperty() : 'id'; - return $dataProvider->fetchAll( + $idProperty = method_exists($dataProvider, 'getIdProperty') ? $dataProvider->getIdProperty() : 'id'; + $collection = $dataProvider->fetchAll( $dataProvider->getEmptyConfig()->setFilter( [ [ @@ -565,6 +630,8 @@ private function getSelectCollection(EnvironmentInterface $environment) ] ) ); + assert($collection instanceof CollectionInterface); + return $collection; } /** @@ -577,8 +644,11 @@ private function getSelectCollection(EnvironmentInterface $environment) */ private function setIntersectProperties(CollectionInterface $collection, EnvironmentInterface $environment) { - $sessionStorage = $environment->getSessionStorage(); $dataDefinition = $environment->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + + $sessionStorage = $environment->getSessionStorage(); + assert($sessionStorage instanceof SessionStorageInterface); $session = $sessionStorage->get($dataDefinition->getName() . '.' . $this->getSubmitAction($environment, true)); @@ -597,12 +667,15 @@ private function setIntersectProperties(CollectionInterface $collection, Environ */ private function setIntersectValues(CollectionInterface $collection, EnvironmentInterface $environment) { - $sessionStorage = $environment->getSessionStorage(); $dataDefinition = $environment->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + + $sessionStorage = $environment->getSessionStorage(); + assert($sessionStorage instanceof SessionStorageInterface); $session = $sessionStorage->get($dataDefinition->getName() . '.' . $this->getSubmitAction($environment, true)); - if (!$session['intersectProperties'] || !\count($session['intersectProperties'])) { + if (!$session['intersectProperties'] || !count($session['intersectProperties'])) { return; } @@ -620,7 +693,10 @@ private function setIntersectValues(CollectionInterface $collection, Environment */ private function collectIntersectModelProperties(CollectionInterface $collection, EnvironmentInterface $environment) { - $palettesDefinition = $environment->getDataDefinition()->getPalettesDefinition(); + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $palettesDefinition = $definition->getPalettesDefinition(); $properties = []; foreach ($collection->getIterator() as $model) { @@ -636,7 +712,13 @@ private function collectIntersectModelProperties(CollectionInterface $collection } } - return \array_filter( + // We always have to keep the id in the array. + $dataProvider = $environment->getDataProvider(); + assert($dataProvider instanceof DataProviderInterface); + $idProperty = method_exists($dataProvider, 'getIdProperty') ? $dataProvider->getIdProperty() : 'id'; + $properties[$idProperty] = $collection->count(); + + return array_filter( $properties, function ($count) use ($collection) { return $count === $collection->count(); @@ -654,29 +736,22 @@ function ($count) use ($collection) { */ private function collectIntersectValues(CollectionInterface $collection, EnvironmentInterface $environment) { - $sessionStorage = $environment->getSessionStorage(); $dataDefinition = $environment->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + + $sessionStorage = $environment->getSessionStorage(); + assert($sessionStorage instanceof SessionStorageInterface); $session = $sessionStorage->get($dataDefinition->getName() . '.' . $this->getSubmitAction($environment, true)); $values = []; foreach ($collection->getIterator() as $model) { - $modelValues = \array_intersect_key($model->getPropertiesAsArray(), $session['intersectProperties']); + $modelValues = array_intersect_key($model->getPropertiesAsArray(), $session['intersectProperties']); foreach ($modelValues as $modelProperty => $modelValue) { - if (1 === $collection->count()) { - $values[$modelProperty] = $modelValue; - - continue; - } - $values[$modelProperty][] = $modelValue; } } - if (1 === $collection->count()) { - return $values; - } - $intersectValues = []; foreach ($values as $propertyName => $propertyValues) { if (!($value = $this->getUniqueValueFromArray($propertyValues))) { @@ -699,14 +774,14 @@ private function collectIntersectValues(CollectionInterface $collection, Environ */ private function getVisibleAndEditAbleProperties(PaletteInterface $palette, ModelInterface $model) { - return \array_intersect( - \array_map( + return array_intersect( + array_map( function (PropertyInterface $property) { return $property->getName(); }, $palette->getVisibleProperties($model) ), - \array_map( + array_map( function (PropertyInterface $property) { return $property->getName(); }, @@ -726,63 +801,63 @@ private function getUniqueValueFromArray(array $values) { $serializedValues = false; foreach ($values as $key => $value) { - if (!\is_array($value)) { + if (!is_array($value)) { continue; } - $values[$key] = \serialize($value); + $values[$key] = serialize($value); $serializedValues = true; } if (!$serializedValues) { - return 1 === \count(\array_unique($values)) ? $values[0] : null; + return 1 === count(array_unique($values)) ? $values[0] : null; } - return 1 === \count(\array_unique($values)) ? \unserialize($values[0], ['allowed_classes' => true]) : null; + return 1 === count(array_unique($values)) ? unserialize($values[0], ['allowed_classes' => true]) : null; } /** * Handle session data for override/edit all. * - * @param array $collection The collection. - * @param string $index The session index for the collection. - * @param EnvironmentInterface $environment The environment. + * @param list $collection The collection. + * @param 'models'|'properties' $index The session index for the collection. + * @param EnvironmentInterface $environment The environment. * - * @return array The collection. + * @return list< + * ModelIdInterface> The collection. */ - private function handleSessionOverrideEditAll(array $collection, $index, EnvironmentInterface $environment) + private function handleSessionOverrideEditAll(array $collection, string $index, EnvironmentInterface $environment) { $dataDefinition = $environment->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + $sessionStorage = $environment->getSessionStorage(); + assert($sessionStorage instanceof SessionStorageInterface); - $session = []; - if ($sessionStorage->has($dataDefinition->getName() . '.' . $this->getSubmitAction($environment, true))) { - $session = - $sessionStorage->get($dataDefinition->getName() . '.' . $this->getSubmitAction($environment, true)); + /** @var array{ + * models: list, + * intersectProperties: array, + * intersectValues: array, + * properties?: list, + * editProperties?: list, + * } $session */ + $session = ['models' => [], 'intersectProperties' => [], 'intersectValues' => []]; + $sessionKey = $dataDefinition->getName() . '.' . $this->getSubmitAction($environment, true); + if ($sessionStorage->has($sessionKey)) { + $session = $sessionStorage->get($sessionKey); } // If collection not empty set to the session and return it. if (!empty($collection)) { - $sessionCollection = \array_map( - function ($item) use ($index) { - if (!\in_array($index, ['models', 'properties'])) { - return $item; - } - - if (!$item instanceof ModelId) { - $item = ModelId::fromSerialized($item); - } - - return $item->getSerialized(); - }, + $sessionCollection = array_map( + static fn (ModelIdInterface $item): string => $item->getSerialized(), $collection ); $session[$index] = $sessionCollection; - $sessionStorage - ->set($dataDefinition->getName() . '.' . $this->getSubmitAction($environment, true), $session); + $sessionStorage->set($sessionKey, $session); return $collection; } @@ -793,17 +868,9 @@ function ($item) use ($index) { } // Get the verify collection from the session and return it. - $collection = \array_map( - function ($item) use ($index) { - if (!\in_array($index, ['models', 'properties'])) { - return $item; - } - - return ModelId::fromSerialized($item); - }, - $session[$index] + return array_map( + static fn (string $item): ModelIdInterface => ModelId::fromSerialized($item), + array_values($session[$index]) ); - - return $collection; } } diff --git a/src/Contao/View/Contao2BackendView/ActionHandler/ShowHandler.php b/src/Contao/View/Contao2BackendView/ActionHandler/ShowHandler.php index 7f6f9ee9..3346d030 100644 --- a/src/Contao/View/Contao2BackendView/ActionHandler/ShowHandler.php +++ b/src/Contao/View/Contao2BackendView/ActionHandler/ShowHandler.php @@ -31,16 +31,26 @@ use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminatorAwareTrait; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\ContaoBackendViewTemplate; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\ViewHelpers; +use ContaoCommunityAlliance\DcGeneral\Controller\ControllerInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\PropertiesDefinitionInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\Properties\PropertyInterface; +use ContaoCommunityAlliance\DcGeneral\Data\DataProviderInterface; use ContaoCommunityAlliance\DcGeneral\Data\ModelId; use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; use ContaoCommunityAlliance\DcGeneral\Data\MultiLanguageDataProviderInterface; use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; use ContaoCommunityAlliance\DcGeneral\Event\ActionEvent; use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralRuntimeException; +use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; +use ContaoCommunityAlliance\DcGeneral\View\ViewInterface; +use ContaoCommunityAlliance\Translator\TranslatorInterface as CcaTranslator; use ContaoCommunityAlliance\Translator\TranslatorInterface; use Contao\StringUtil; +use Contao\System; use LogicException; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; +use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface; use function array_merge; use function array_values; @@ -55,14 +65,61 @@ class ShowHandler { use RequestScopeDeterminatorAwareTrait; + /** + * The token manager. + * + * @var CsrfTokenManagerInterface + */ + private CsrfTokenManagerInterface $tokenManager; + + /** + * The token name. + * + * @var string + */ + private string $tokenName; + /** * ShowHandler constructor. * - * @param RequestScopeDeterminator $scopeDeterminator The request mode determinator. + * @param RequestScopeDeterminator $scopeDeterminator The request mode determinator. + * @param CsrfTokenManagerInterface|null $tokenManager The token manager. + * @param string|null $tokenName The token name. */ - public function __construct(RequestScopeDeterminator $scopeDeterminator) - { + public function __construct( + RequestScopeDeterminator $scopeDeterminator, + ?CsrfTokenManagerInterface $tokenManager = null, + ?string $tokenName = null + ) { $this->setScopeDeterminator($scopeDeterminator); + + if (null === $tokenManager) { + $tokenManager = System::getContainer()->get('security.csrf.token_manager'); + assert($tokenManager instanceof CsrfTokenManagerInterface); + + // @codingStandardsIgnoreStart + @trigger_error( + 'Not passing the csrf token manager as 4th argument to "' . __METHOD__ . '" is deprecated ' . + 'and will cause an error in DCG 3.0', + E_USER_DEPRECATED + ); + // @codingStandardsIgnoreEnd + } + if (null === $tokenName) { + $tokenName = System::getContainer()->getParameter('contao.csrf_token_name'); + assert(\is_string($tokenName)); + + // @codingStandardsIgnoreStart + @trigger_error( + 'Not passing the csrf token name as 5th argument to "' . __METHOD__ . '" is deprecated ' . + 'and will cause an error in DCG 3.0', + E_USER_DEPRECATED + ); + // @codingStandardsIgnoreEnd + } + + $this->tokenManager = $tokenManager; + $this->tokenName = $tokenName; } /** @@ -87,22 +144,31 @@ public function handleEvent(ActionEvent $event) */ protected function getModel(EnvironmentInterface $environment) { - $modelId = ModelId::fromSerialized($environment->getInputProvider()->getParameter('id')); + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + + $modelId = ModelId::fromSerialized($inputProvider->getParameter('id')); $dataProvider = $environment->getDataProvider($modelId->getDataProviderName()); - $model = $dataProvider->fetch($dataProvider->getEmptyConfig()->setId($modelId->getId())); + assert($dataProvider instanceof DataProviderInterface); + + $model = $dataProvider->fetch($dataProvider->getEmptyConfig()->setId($modelId->getId())); if ($model) { return $model; } $eventDispatcher = $environment->getEventDispatcher(); + assert($eventDispatcher instanceof EventDispatcherInterface); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); $eventDispatcher->dispatch( new LogEvent( sprintf( 'Could not find ID %s in %s. DC_General show()', $modelId->getId(), - $environment->getDataDefinition()->getName() + $definition->getName() ), __CLASS__ . '::' . __FUNCTION__, 'ERROR' @@ -126,17 +192,20 @@ protected function getModel(EnvironmentInterface $environment) protected function getPropertyLabel(EnvironmentInterface $environment, PropertyInterface $property) { $translator = $environment->getTranslator(); - $key = $property->getLabel(); + assert($translator instanceof TranslatorInterface); - if (null === $key) { + $key = $property->getLabel(); + + if ('' === $key) { throw new LogicException('Missing label for property ' . $property->getName()); } - if ('' !== $key) { - $label = $translator->translate($key, $environment->getDataDefinition()->getName()); - if ($label !== $key) { - return $label; - } + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $label = $translator->translate($key, $definition->getName()); + if ($label !== $key) { + return $label; } $mscKey = 'MSC.' . $property->getName() . '.0'; @@ -161,7 +230,11 @@ protected function getPropertyLabel(EnvironmentInterface $environment, PropertyI protected function convertModel(ModelInterface $model, EnvironmentInterface $environment): array { $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + $properties = $definition->getPropertiesDefinition(); + assert($properties instanceof PropertiesDefinitionInterface); + $palette = $definition->getPalettesDefinition()->findPalette($model); $values = [ 'system' => [], @@ -175,9 +248,10 @@ protected function convertModel(ModelInterface $model, EnvironmentInterface $env // Add only visible properties. foreach ($palette->getVisibleProperties($model) as $paletteProperty) { $palettePropertyName = $paletteProperty->getName(); - if (!($visibleProperty = $properties->getProperty($palettePropertyName))) { - throw new DcGeneralRuntimeException('Unable to retrieve property ' . $paletteProperty->getName()); + if (!$properties->hasProperty($palettePropertyName)) { + throw new DcGeneralRuntimeException('Unable to retrieve property ' . $palettePropertyName); } + $visibleProperty = $properties->getProperty($palettePropertyName); // Make it human-readable. $values['visible'][$palettePropertyName] = ViewHelpers::getReadableFieldValue( @@ -196,14 +270,9 @@ protected function convertModel(ModelInterface $model, EnvironmentInterface $env if (isset($values['visible'][$propertyName])) { continue; } - - if (!($systemProperty = $properties->getProperty($propertyName))) { - throw new DcGeneralRuntimeException('Unable to retrieve property ' . $propertyName); - } - $values['system'][$propertyName] = $model->getProperty($propertyName); $labels['system'][$propertyName] = - sprintf('%s [%s]', $this->getPropertyLabel($environment, $systemProperty), $propertyName); + sprintf('%s [%s]', $this->getPropertyLabel($environment, $property), $propertyName); } return [ @@ -249,27 +318,45 @@ protected function getHeadline(TranslatorInterface $translator, $model) */ protected function process(Action $action, EnvironmentInterface $environment) { - if ($environment->getDataDefinition()->getBasicDefinition()->isEditOnlyMode()) { - return $environment->getView()->edit($action); + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $view = $environment->getView(); + assert($view instanceof ViewInterface); + + if ($definition->getBasicDefinition()->isEditOnlyMode()) { + return $view->edit($action); } - $modelId = ModelId::fromSerialized($environment->getInputProvider()->getParameter('id')); + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + + $modelId = ModelId::fromSerialized($inputProvider->getParameter('id')); $dataProvider = $environment->getDataProvider($modelId->getDataProviderName()); - $translator = $environment->getTranslator(); - $model = $this->getModel($environment); - $data = $this->convertModel($model, $environment); + + $translator = $environment->getTranslator(); + assert($translator instanceof TranslatorInterface); + + $model = $this->getModel($environment); + assert($model instanceof ModelInterface); + + $data = $this->convertModel($model, $environment); $template = (new ContaoBackendViewTemplate('dcbe_general_show')) ->set('headline', $this->getHeadline($translator, $model)) ->set('arrFields', $data['values']) ->set('arrLabels', $data['labels']); + $controller = $environment->getController(); + assert($controller instanceof ControllerInterface); + if ($dataProvider instanceof MultiLanguageDataProviderInterface) { $template - ->set('languages', $environment->getController()->getSupportedLanguages($model->getId())) + ->set('languages', $controller->getSupportedLanguages($model->getId())) ->set('currentLanguage', $dataProvider->getCurrentLanguage()) ->set('languageSubmit', $translator->translate('MSC.showSelected')) - ->set('backBT', StringUtil::specialchars($translator->translate('MSC.backBT'))); + ->set('backBT', StringUtil::specialchars($translator->translate('MSC.backBT'))) + ->set('REQUEST_TOKEN', $this->tokenManager->getToken($this->tokenName)); } else { $template->set('languages', null); } diff --git a/src/Contao/View/Contao2BackendView/ActionHandler/ToggleHandler.php b/src/Contao/View/Contao2BackendView/ActionHandler/ToggleHandler.php index 1dda36f1..6d497bfd 100644 --- a/src/Contao/View/Contao2BackendView/ActionHandler/ToggleHandler.php +++ b/src/Contao/View/Contao2BackendView/ActionHandler/ToggleHandler.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2021 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -16,7 +16,7 @@ * @author David Molineus * @author Benedict Zinke * @author Ingolf Steinhardt - * @copyright 2013-2021 Contao Community Alliance. + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -27,9 +27,13 @@ use ContaoCommunityAlliance\DcGeneral\Contao\DataDefinition\Definition\Contao2BackendViewDefinitionInterface; use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminator; use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminatorAwareTrait; +use ContaoCommunityAlliance\DcGeneral\Data\ConfigInterface; +use ContaoCommunityAlliance\DcGeneral\Data\DataProviderInterface; use ContaoCommunityAlliance\DcGeneral\Data\ModelId; use ContaoCommunityAlliance\DcGeneral\Data\ModelIdInterface; +use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; use ContaoCommunityAlliance\DcGeneral\Data\MultiLanguageDataProviderInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\CommandInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\ToggleCommandInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\TranslatedToggleCommandInterface; @@ -38,6 +42,7 @@ use ContaoCommunityAlliance\DcGeneral\Event\PostPersistModelEvent; use ContaoCommunityAlliance\DcGeneral\Event\PrePersistModelEvent; use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; /** * This class handles toggle commands. @@ -110,7 +115,12 @@ protected function process( ModelIdInterface $modelId = null ) { $dataProvider = $environment->getDataProvider(); - $newState = $this->determineNewState($environment->getInputProvider(), $operation->isInverse()); + assert($dataProvider instanceof DataProviderInterface); + + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + + $newState = $this->determineNewState($inputProvider, $operation->isInverse()); // Override the language for language aware toggling. if ( @@ -122,11 +132,21 @@ protected function process( $dataProvider->setCurrentLanguage($operation->getLanguage()); } - $model = $dataProvider->fetch($dataProvider->getEmptyConfig()->setId($modelId->getId())); + $provider = $dataProvider->getEmptyConfig(); + assert($provider instanceof ConfigInterface); + assert($modelId instanceof ModelIdInterface); + $config = $provider->setId($modelId->getId()); + assert($config instanceof ConfigInterface); + + $model = $dataProvider->fetch($config); + assert($model instanceof ModelInterface); + $originalModel = clone $model; $originalModel->setId($model->getId()); $model->setProperty($operation->getToggleProperty(), $newState); + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); $dispatcher->dispatch( new PrePersistModelEvent($environment, $model, $originalModel), @@ -142,6 +162,7 @@ protected function process( // Select the previous language. if (isset($language)) { + /** @var MultiLanguageDataProviderInterface $dataProvider */ $dataProvider->setCurrentLanguage($language); } } @@ -159,16 +180,22 @@ private function checkPermission(ActionEvent $event, ToggleCommandInterface $com { $environment = $event->getEnvironment(); - if (true === $environment->getDataDefinition()->getBasicDefinition()->isEditable() && !$command->isDisabled()) { + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + if (true === !$command->isDisabled() && $definition->getBasicDefinition()->isEditable()) { return true; } + $operation = $this->getOperation($event->getAction(), $environment); + assert($operation instanceof ToggleCommandInterface); + $event->setResponse( \sprintf( '
You have no permission for toggle %s.
', - $this->getOperation($event->getAction(), $environment)->getToggleProperty() + $operation->getToggleProperty() ) ); @@ -185,12 +212,16 @@ private function checkPermission(ActionEvent $event, ToggleCommandInterface $com private function getModelId(EnvironmentInterface $environment) { $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); if ($inputProvider->hasParameter('id') && $inputProvider->getParameter('id')) { $modelId = ModelId::fromSerialized($inputProvider->getParameter('id')); } - if (!(isset($modelId) && ($environment->getDataDefinition()->getName() === $modelId->getDataProviderName()))) { + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + if (!(isset($modelId) && ($definition->getName() === $modelId->getDataProviderName()))) { return null; } @@ -203,14 +234,18 @@ private function getModelId(EnvironmentInterface $environment) * @param Action $action The action. * @param EnvironmentInterface $environment The environment. * - * @return CommandInterface + * @return CommandInterface|null */ private function getOperation(Action $action, EnvironmentInterface $environment) { - /** @var Contao2BackendViewDefinitionInterface $definition */ - $definition = $environment->getDataDefinition()->getDefinition(Contao2BackendViewDefinitionInterface::NAME); - $name = $action->getName(); - $commands = $definition->getModelCommands(); + $dataDefinition = $environment->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + + $definition = $dataDefinition->getDefinition(Contao2BackendViewDefinitionInterface::NAME); + assert($definition instanceof Contao2BackendViewDefinitionInterface); + + $name = $action->getName(); + $commands = $definition->getModelCommands(); if (!$commands->hasCommandNamed($name)) { return null; diff --git a/src/Contao/View/Contao2BackendView/BaseView.php b/src/Contao/View/Contao2BackendView/BaseView.php index 332d4bb0..ca5ea06c 100644 --- a/src/Contao/View/Contao2BackendView/BaseView.php +++ b/src/Contao/View/Contao2BackendView/BaseView.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2021 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -18,7 +18,8 @@ * @author David Molineus * @author Martin Treml * @author Sven Baumann - * @copyright 2013-2021 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -36,6 +37,8 @@ use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\GetGroupHeaderEvent; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\GetSelectModeButtonsEvent; use ContaoCommunityAlliance\DcGeneral\Controller\Ajax3X; +use ContaoCommunityAlliance\DcGeneral\Controller\ControllerInterface; +use ContaoCommunityAlliance\DcGeneral\Data\DataProviderInterface; use ContaoCommunityAlliance\DcGeneral\Data\ModelId; use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; @@ -44,13 +47,17 @@ use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; use ContaoCommunityAlliance\DcGeneral\Event\ActionEvent; use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralRuntimeException; +use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; use ContaoCommunityAlliance\DcGeneral\Panel\PanelContainerInterface; +use ContaoCommunityAlliance\DcGeneral\SessionStorageInterface; +use ContaoCommunityAlliance\Translator\TranslatorInterface; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\EventDispatcher\EventSubscriberInterface; /** * Class BaseView. * - * This class is the base class for the different backend view mode sub classes. + * This class is the base class for the different backend view mode sub-classes. * * @SuppressWarnings(PHPMD.TooManyPublicMethods) * @SuppressWarnings(PHPMD.CouplingBetweenObjects) @@ -118,10 +125,22 @@ public function handleAction(ActionEvent $event) { $GLOBALS['TL_CSS']['cca.dc-general.generalDriver'] = 'bundles/ccadcgeneral/css/generalDriver.css'; + $environment = $event->getEnvironment(); + assert($environment instanceof EnvironmentInterface); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $environment2 = $this->getEnvironment(); + assert($environment2 instanceof EnvironmentInterface); + + $definition2 = $environment2->getDataDefinition(); + assert($definition2 instanceof ContainerInterface); + if ( (null !== $event->getResponse()) - || $event->getEnvironment()->getDataDefinition()->getName() - !== $this->environment->getDataDefinition()->getName() + || $definition->getName() + !== $definition2->getName() ) { return; } @@ -139,7 +158,7 @@ public function handleAction(ActionEvent $event) if ('showAll' === $name) { $response = \call_user_func_array( [$this, $name], - \array_merge([$action], $action->getArguments()) + \array_values(\array_merge([$action], $action->getArguments())) ); $event->setResponse($response); @@ -172,12 +191,15 @@ public function handleAction(ActionEvent $event) */ public function setEnvironment(EnvironmentInterface $environment) { - if ($this->getEnvironment()) { - $this->environment->getEventDispatcher()->removeSubscriber($this); - } + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $dispatcher->removeSubscriber($this); $this->environment = $environment; - $this->getEnvironment()->getEventDispatcher()->addSubscriber($this); + $dispatcher->addSubscriber($this); + + return $this; } /** @@ -195,7 +217,13 @@ public function getEnvironment() */ protected function getDataDefinition() { - return $this->getEnvironment()->getDataDefinition(); + $environment = $this->getEnvironment(); + assert($environment instanceof EnvironmentInterface); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + return $definition; } /** @@ -208,7 +236,13 @@ protected function getDataDefinition() */ protected function translate($path, $section = null) { - return $this->getEnvironment()->getTranslator()->translate($path, $section); + $environment = $this->getEnvironment(); + assert($environment instanceof EnvironmentInterface); + + $translator = $environment->getTranslator(); + assert($translator instanceof TranslatorInterface); + + return $translator->translate($path, $section); } /** @@ -220,7 +254,12 @@ protected function translate($path, $section = null) */ protected function translateFallback($path) { - $translator = $this->getEnvironment()->getTranslator(); + $environment = $this->getEnvironment(); + assert($environment instanceof EnvironmentInterface); + + $translator = $environment->getTranslator(); + assert($translator instanceof TranslatorInterface); + // Try via definition name as domain first. $value = $translator->translate($path, $this->getDataDefinition()->getName()); if ($value !== $path) { @@ -271,7 +310,16 @@ public function getPanel() */ protected function getViewSection() { - return $this->getDataDefinition()->getDefinition(Contao2BackendViewDefinitionInterface::NAME); + $environment = $this->getEnvironment(); + assert($environment instanceof EnvironmentInterface); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $backendView = $definition->getDefinition(Contao2BackendViewDefinitionInterface::NAME); + assert($backendView instanceof Contao2BackendViewDefinitionInterface); + + return $backendView; } /** @@ -281,7 +329,13 @@ protected function getViewSection() */ protected function isSelectModeActive() { - return 'select' === $this->getEnvironment()->getInputProvider()->getParameter('act'); + $environment = $this->getEnvironment(); + assert($environment instanceof EnvironmentInterface); + + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + + return 'select' === $inputProvider->getParameter('act'); } /** @@ -293,20 +347,26 @@ protected function isSelectModeActive() * @param int $groupLength The length of the value to use for grouping (only used when grouping mode is * ListingConfigInterface::GROUP_CHAR). * - * @return string + * @return string|null */ public function formatCurrentValue($field, ModelInterface $model, $groupMode, $groupLength) { $environment = $this->getEnvironment(); - $property = $environment->getDataDefinition()->getPropertiesDefinition()->getProperty($field); + assert($environment instanceof EnvironmentInterface); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); // No property? Get out! - if (!$property) { + if (!$definition->getPropertiesDefinition()->hasProperty($field)) { return '-'; } $event = new GetGroupHeaderEvent($environment, $model, $field, null, $groupMode, $groupLength); - $environment->getEventDispatcher()->dispatch($event, $event::NAME); + + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + $dispatcher->dispatch($event, $event::NAME); return $event->getValue(); } @@ -319,10 +379,14 @@ public function formatCurrentValue($field, ModelInterface $model, $groupMode, $g protected function getSelectButtons() { $environment = $this->getEnvironment(); + assert($environment instanceof EnvironmentInterface); $event = new GetSelectModeButtonsEvent($environment); $event->setButtons([]); - $environment->getEventDispatcher()->dispatch($event, GetSelectModeButtonsEvent::NAME); + + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + $dispatcher->dispatch($event, GetSelectModeButtonsEvent::NAME); return $event->getButtons(); } @@ -336,7 +400,13 @@ protected function getSelectButtons() */ protected function isMultiLanguage($currentID) { - return (bool) \count($this->getEnvironment()->getController()->getSupportedLanguages($currentID)); + $environment = $this->getEnvironment(); + assert($environment instanceof EnvironmentInterface); + + $controller = $environment->getController(); + assert($controller instanceof ControllerInterface); + + return (bool) \count($controller->getSupportedLanguages($currentID)); } /** @@ -348,7 +418,13 @@ protected function isMultiLanguage($currentID) */ protected function getTemplate($name) { - return (new ContaoBackendViewTemplate($name))->setTranslator($this->getEnvironment()->getTranslator()); + $environment = $this->getEnvironment(); + assert($environment instanceof EnvironmentInterface); + + $translator = $environment->getTranslator(); + assert($translator instanceof TranslatorInterface); + + return (new ContaoBackendViewTemplate($name))->setTranslator($translator); } /** @@ -361,7 +437,10 @@ protected function getTemplate($name) public function handleAjaxCall() { $environment = $this->getEnvironment(); - $input = $environment->getInputProvider(); + assert($environment instanceof EnvironmentInterface); + + $input = $environment->getInputProvider(); + assert($input instanceof InputProviderInterface); if (true === ($input->hasParameter('id'))) { // Redefine the parameter id if this isn´t model id conform. @@ -371,7 +450,9 @@ public function handleAjaxCall() } $modelId = ModelId::fromSerialized($input->getParameter('id')); $dataProvider = $environment->getDataProvider($modelId->getDataProviderName()); - $model = $dataProvider->fetch($dataProvider->getEmptyConfig()->setId($modelId->getId())); + assert($dataProvider instanceof DataProviderInterface); + + $model = $dataProvider->fetch($dataProvider->getEmptyConfig()->setId($modelId->getId())); } $this->addAjaxPropertyForEditAll(); @@ -421,11 +502,18 @@ public function delete(Action $action) */ public function move(Action $action) { - if ($this->getEnvironment()->getDataDefinition()->getBasicDefinition()->isEditOnlyMode()) { + $environment = $this->getEnvironment(); + assert($environment instanceof EnvironmentInterface); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + + if ($definition->getBasicDefinition()->isEditOnlyMode()) { return $this->edit($action); } - return \vsprintf($this->notImplMsg, 'move - Mode'); + return \vsprintf($this->notImplMsg, ['move - Mode']); } /** @@ -433,11 +521,17 @@ public function move(Action $action) */ public function undo(Action $action) { - if ($this->getEnvironment()->getDataDefinition()->getBasicDefinition()->isEditOnlyMode()) { + $environment = $this->getEnvironment(); + assert($environment instanceof EnvironmentInterface); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + if ($definition->getBasicDefinition()->isEditOnlyMode()) { return $this->edit($action); } - return \vsprintf($this->notImplMsg, 'undo - Mode'); + return \vsprintf($this->notImplMsg, ['undo - Mode']); } /** @@ -463,13 +557,19 @@ public function edit(Action $action) */ public function showAll(Action $action) { - if ($this->getEnvironment()->getDataDefinition()->getBasicDefinition()->isEditOnlyMode()) { + $environment = $this->getEnvironment(); + assert($environment instanceof EnvironmentInterface); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + if ($definition->getBasicDefinition()->isEditOnlyMode()) { return $this->edit($action); } return \sprintf( $this->notImplMsg, - 'showAll - Mode ' . $this->getEnvironment()->getDataDefinition()->getBasicDefinition()->getMode() + 'showAll - Mode ' . (string) ($definition->getBasicDefinition()->getMode() ?? '') ); } @@ -480,7 +580,10 @@ public function showAll(Action $action) */ protected function generateHeaderButtons() { - return (new GlobalButtonRenderer($this->getEnvironment()))->render(); + $environment = $this->getEnvironment(); + assert($environment instanceof EnvironmentInterface); + + return (new GlobalButtonRenderer($environment))->render(); } /** @@ -508,14 +611,18 @@ protected function panel($ignoredPanels = []) public function breadcrumb() { $environment = $this->getEnvironment(); + assert($environment instanceof EnvironmentInterface); + + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); $event = new GetBreadcrumbEvent($environment); - $environment->getEventDispatcher()->dispatch($event, $event::NAME); + $dispatcher->dispatch($event, $event::NAME); $elements = $event->getElements(); - if (!\is_array($elements) || !\count($elements)) { - return null; + if (!\count($elements)) { + return ''; } $GLOBALS['TL_CSS']['cca.dc-general.generalBreadcrumb'] = 'bundles/ccadcgeneral/css/generalBreadcrumb.css'; @@ -532,7 +639,14 @@ public function breadcrumb() */ private function addAjaxPropertyForEditAll() { - $inputProvider = $this->getEnvironment()->getInputProvider(); + $environment = $this->getEnvironment(); + assert($environment instanceof EnvironmentInterface); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); if ( ('select' !== $inputProvider->getParameter('act')) @@ -547,7 +661,7 @@ private function addAjaxPropertyForEditAll() return; } - $propertiesDefinition = $this->getEnvironment()->getDataDefinition()->getPropertiesDefinition(); + $propertiesDefinition = $definition->getPropertiesDefinition(); $propertyClass = \get_class($originalProperty); @@ -568,22 +682,32 @@ private function addAjaxPropertyForEditAll() /** * Find the original property by the modelId. * - * @param string $propertyName The property name. + * @param string|null $propertyName The property name. * * @return PropertyInterface|null */ - private function findOriginalPropertyByModelId($propertyName) + private function findOriginalPropertyByModelId(?string $propertyName): ?PropertyInterface { if (null === $propertyName) { return null; } - $inputProvider = $this->getEnvironment()->getInputProvider(); - $sessionStorage = $this->getEnvironment()->getSessionStorage(); + $environment = $this->getEnvironment(); + assert($environment instanceof EnvironmentInterface); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + + $sessionStorage = $environment->getSessionStorage(); + assert($sessionStorage instanceof SessionStorageInterface); $selectAction = $inputProvider->getParameter('select'); - $session = $sessionStorage->get($this->getEnvironment()->getDataDefinition()->getName() . '.' . $selectAction); + /** @var array{models: list} $session */ + $session = $sessionStorage->get($definition->getName() . '.' . $selectAction); $originalPropertyName = null; foreach ($session['models'] as $modelId) { @@ -599,7 +723,11 @@ private function findOriginalPropertyByModelId($propertyName) $originalPropertyName = \substr($propertyName, \strlen($propertyNamePrefix)); } - $propertiesDefinition = $this->getEnvironment()->getDataDefinition()->getPropertiesDefinition(); + if (!$originalPropertyName) { + return null; + } + + $propertiesDefinition = $definition->getPropertiesDefinition(); if (!$propertiesDefinition->hasProperty($originalPropertyName)) { return null; } diff --git a/src/Contao/View/Contao2BackendView/ButtonRenderer.php b/src/Contao/View/Contao2BackendView/ButtonRenderer.php index de0de205..a3650ff2 100644 --- a/src/Contao/View/Contao2BackendView/ButtonRenderer.php +++ b/src/Contao/View/Contao2BackendView/ButtonRenderer.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2022 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -17,7 +17,7 @@ * @author Ingolf Steinhardt * @author Richard Henkenjohann * @author David Molineus - * @copyright 2013-2022 Contao Community Alliance. + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -31,16 +31,19 @@ use ContaoCommunityAlliance\Contao\Bindings\ContaoEvents; use ContaoCommunityAlliance\Contao\Bindings\Events\Backend\AddToUrlEvent; use ContaoCommunityAlliance\Contao\Bindings\Events\Image\GenerateHtmlEvent; +use ContaoCommunityAlliance\DcGeneral\Clipboard\ClipboardInterface; use ContaoCommunityAlliance\DcGeneral\Clipboard\Filter; use ContaoCommunityAlliance\DcGeneral\Clipboard\ItemInterface; use ContaoCommunityAlliance\DcGeneral\Contao\DataDefinition\Definition\Contao2BackendViewDefinitionInterface; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\GetOperationButtonEvent; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\GetPasteButtonEvent; +use ContaoCommunityAlliance\DcGeneral\Controller\ControllerInterface; use ContaoCommunityAlliance\DcGeneral\Controller\ModelCollector; use ContaoCommunityAlliance\DcGeneral\Data\CollectionInterface; use ContaoCommunityAlliance\DcGeneral\Data\ModelId; use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; use ContaoCommunityAlliance\DcGeneral\Data\MultiLanguageDataProviderInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\CommandCollectionInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\CommandInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\CopyCommandInterface; @@ -48,11 +51,13 @@ use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\ToggleCommandInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\TranslatedToggleCommandInterface; use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; +use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralInvalidArgumentException; +use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; use ContaoCommunityAlliance\Translator\TranslatorInterface; use Symfony\Component\EventDispatcher\EventDispatcherInterface; /** - * This class is an helper for rendering the operation buttons in the views. + * This class is a helper for rendering the operation buttons in the views. * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) @@ -62,14 +67,14 @@ class ButtonRenderer /** * The ids of all circular contained models. * - * @var string[] + * @var list */ private $circularModelIds; /** * The clipboard items in use. * - * @var ItemInterface[] + * @var list */ private $clipboardItems; @@ -115,23 +120,37 @@ class ButtonRenderer */ public function __construct(EnvironmentInterface $environment) { - $this->environment = $environment; - $this->translator = $environment->getTranslator(); - $this->eventDispatcher = $environment->getEventDispatcher(); - $this->clipboardItems = $this->calculateClipboardItems(); - $dataDefinition = $environment->getDataDefinition(); - $this->commands = $dataDefinition - ->getDefinition(Contao2BackendViewDefinitionInterface::NAME) - ->getModelCommands(); - $controller = $environment->getController(); + $this->environment = $environment; + + $translator = $environment->getTranslator(); + assert($translator instanceof TranslatorInterface); + $this->translator = $translator; + + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + $this->eventDispatcher = $dispatcher; + + + $dataDefinition = $environment->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + + $this->clipboardItems = $this->calculateClipboardItems(); + + $backendView = $dataDefinition->getDefinition(Contao2BackendViewDefinitionInterface::NAME); + assert($backendView instanceof Contao2BackendViewDefinitionInterface); + + $this->commands = $backendView->getModelCommands(); + $controller = $environment->getController(); + assert($controller instanceof ControllerInterface); + $this->clipboardModels = $controller->getModelsFromClipboardItems($this->clipboardItems); $this->circularModelIds = []; // We must only check for CUT operation here as pasting copy'ed parents is allowed. - $cutItems = \array_filter($this->clipboardItems, function ($item) { - /** @var ItemInterface $item */ - return $item->getAction() === $item::CUT; - }); + $cutItems = \array_values(\array_filter( + $this->clipboardItems, + static fn (ItemInterface $item): bool => $item->getAction() === $item::CUT + )); $cutModels = $controller->getModelsFromClipboardItems($cutItems); $collector = new ModelCollector($environment); foreach ($cutModels as $model) { @@ -163,9 +182,9 @@ public function renderButtonsForCollection(CollectionInterface $collection) /** * Render the operation buttons for the passed model. * - * @param ModelInterface $model The model to render the buttons for. - * @param ModelInterface $previous The previous model in the collection. - * @param ModelInterface $next The next model in the collection. + * @param ModelInterface $model The model to render the buttons for. + * @param ModelInterface|null $previous The previous model in the collection. + * @param ModelInterface|null $next The next model in the collection. * * @return void */ @@ -203,14 +222,18 @@ private function renderButtonsFor( $buttonEvent ->setModel($model) ->setCircularReference($isCircular) - ->setPrevious($previous) - ->setNext($next) ->setHrefAfter($urlAfter) ->setHrefInto($urlInto) // Check if the id is in the ignore list. ->setPasteAfterDisabled($isCircular) ->setPasteIntoDisabled($isCircular) ->setContainedModels($this->clipboardModels); + if (null !== $previous) { + $buttonEvent->setPrevious($previous); + } + if (null !== $next) { + $buttonEvent->setNext($next); + } $this->eventDispatcher->dispatch($buttonEvent, GetPasteButtonEvent::NAME); $buttons['pasteafter'] = $this->renderPasteAfterButton($buttonEvent); @@ -233,6 +256,8 @@ private function renderButtonsFor( private function isHierarchical() { $dataDefinition = $this->environment->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + $basicDefinition = $dataDefinition->getBasicDefinition(); return $basicDefinition::MODE_HIERARCHICAL === $basicDefinition->getMode(); @@ -256,8 +281,11 @@ private function hasPasteButtons() */ private function hasPasteNewButton() { - $environment = $this->environment; - $basicDefinition = $environment->getDataDefinition()->getBasicDefinition(); + $environment = $this->environment; + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $basicDefinition = $definition->getBasicDefinition(); return ((true === (bool) ViewHelpers::getManualSortingProperty($environment)) && (true === empty($this->clipboardItems)) @@ -268,13 +296,13 @@ private function hasPasteNewButton() /** * Render a command button. * - * @param CommandInterface $command The command to render the button for. - * @param ModelInterface $model The model to which the command shall get applied. - * @param ModelInterface $previous The previous model in the collection. - * @param ModelInterface $next The next model in the collection. - * @param bool $isCircularReference Determinator if there exists a circular reference between the model - * and the model(s) contained in the clipboard. - * @param string[] $childIds The ids of all child models. + * @param CommandInterface $command The command to render the button for. + * @param ModelInterface $model The model to which the command shall get applied. + * @param ModelInterface|null $previous The previous model in the collection. + * @param ModelInterface|null $next The next model in the collection. + * @param bool $isCircularReference Determinator if there exists a circular reference between the + * model and the model(s) contained in the clipboard. + * @param string[] $childIds The ids of all child models. * * @return string */ @@ -309,7 +337,7 @@ private function buildCommand($command, $model, $previous, $next, $isCircularRef ->setObjModel($model) ->setAttributes($attributes) ->setLabel($this->getCommandLabel($command)) - ->setTitle(\sprintf($this->translate((string) $command->getDescription()), $model->getID())) + ->setTitle(\sprintf($this->translate($command->getDescription()), $model->getID())) ->setHref($this->calculateHref($command, $model)) ->setChildRecordIds($childIds) ->setCircularReference($isCircularReference) @@ -331,12 +359,12 @@ private function buildCommand($command, $model, $previous, $next, $isCircularRef if ($icon !== Image::getPath($icon)) { $iconDisabledSuffix = '_'; } - $icon = \substr_replace($icon, $iconDisabledSuffix, \strrpos($icon, '.'), 0); + $icon = \substr_replace($icon, $iconDisabledSuffix, \strrpos($icon, '.') ?: \strlen($icon), 0); } return $this->renderImageAsHtml( $icon, - ($buttonEvent->getLabel() ?? ''), + $buttonEvent->getLabel(), \sprintf( 'title="%s" class="%s"', StringUtil::specialchars($this->translator->translate( @@ -352,10 +380,10 @@ private function buildCommand($command, $model, $previous, $next, $isCircularRef return \sprintf( ' %s', $command->getName(), - $buttonEvent->getHref(), + $buttonEvent->getHref() ?? '', StringUtil::specialchars($buttonEvent->getTitle()), \ltrim($buttonEvent->getAttributes()), - $this->renderImageAsHtml($icon, ($buttonEvent->getLabel() ?? '')) + $this->renderImageAsHtml($icon, $buttonEvent->getLabel()) ); } @@ -408,6 +436,8 @@ private function calculateParameters(CommandInterface $command, $serializedModel $parameters['act'] = $command->getName(); $inputProvider = $this->environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + // If we have a pid add it, used for mode 4 and all parent -> current views. if ($inputProvider->hasParameter('pid')) { $parameters['pid'] = $inputProvider->getParameter('pid'); @@ -462,15 +492,18 @@ private function renderPasteIntoButton(GetPasteButtonEvent $event) return $this->renderImageAsHtml('pasteinto_.svg', $label, 'class="blink"'); } + $model = $event->getModel(); + assert($model instanceof ModelInterface); + if ('pasteinto.1' !== ($opDesc = $this->translate('pasteinto.1'))) { - $title = \sprintf($opDesc, $event->getModel()->getId()); + $title = \sprintf($opDesc, $model->getId()); } else { - $title = \sprintf('%s id %s', $label, $event->getModel()->getId()); + $title = \sprintf('%s id %s', $label, $model->getId()); } return \sprintf( ' %s', - $event->getHrefInto(), + $event->getHrefInto() ?? '', StringUtil::specialchars($title), $this->renderImageAsHtml('pasteinto.svg', $label, 'class="blink"') ); @@ -494,15 +527,18 @@ private function renderPasteAfterButton(GetPasteButtonEvent $event) return $this->renderImageAsHtml('pasteafter_.svg', $label, 'class="blink"'); } + $model = $event->getModel(); + assert($model instanceof ModelInterface); + if ('pasteafter.1' !== ($opDesc = $this->translate('pasteafter.1'))) { - $title = \sprintf($opDesc, $event->getModel()->getId()); + $title = \sprintf($opDesc, $model->getId()); } else { - $title = \sprintf('%s id %s', $label, $event->getModel()->getId()); + $title = \sprintf('%s id %s', $label, $model->getId()); } return \sprintf( ' %s', - $event->getHrefAfter(), + $event->getHrefAfter() ?? '', StringUtil::specialchars($title), $this->renderImageAsHtml('pasteafter.svg', $label, 'class="blink"') ); @@ -511,16 +547,23 @@ private function renderPasteAfterButton(GetPasteButtonEvent $event) /** * Calculate all clipboard items for the current view. * - * @return ItemInterface[] + * @return list */ private function calculateClipboardItems() { $dataDefinition = $this->environment->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + $basicDefinition = $dataDefinition->getBasicDefinition(); $clipboard = $this->environment->getClipboard(); + assert($clipboard instanceof ClipboardInterface); $filter = new Filter(); - $filter->andModelIsFromProvider($basicDefinition->getDataProvider()); + + $dataProvider = $basicDefinition->getDataProvider(); + assert(\is_string($dataProvider)); + + $filter->andModelIsFromProvider($dataProvider); if ($parentProviderName = $basicDefinition->getParentDataProvider()) { $filter->andParentIsFromProvider($parentProviderName); } else { @@ -539,7 +582,10 @@ private function calculateClipboardItems() */ protected function translate($path) { - $value = $this->translator->translate($path, $this->environment->getDataDefinition()->getName()); + $definition = $this->environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $value = $this->translator->translate($path, $definition->getName()); if ($path !== $value) { return $value; } @@ -564,7 +610,7 @@ private function renderImageAsHtml($src, $alt, $attributes = '') ContaoEvents::IMAGE_GET_HTML ); - return $imageEvent->getHtml(); + return $imageEvent->getHtml() ?? ''; } /** @@ -592,8 +638,9 @@ private function addToUrl($parameters) */ private function isTogglerInActiveState($command, $model) { - $dataProvider = $this->environment->getDataProvider($model->getProviderName()); - $propModel = $model; + $dataProvider = $this->environment->getDataProvider($model->getProviderName()); + $propModel = $model; + $toggleProperty = $command->getToggleProperty(); if ( $command instanceof TranslatedToggleCommandInterface @@ -605,16 +652,19 @@ private function isTogglerInActiveState($command, $model) $dataProvider ->getEmptyConfig() ->setId($model->getId()) - ->setFields([$command->getToggleProperty()]) + ->setFields([$toggleProperty]) ); + if (null === $propModel) { + throw new DcGeneralInvalidArgumentException('Model not found: ' . $model->getId()); + } $dataProvider->setCurrentLanguage($language); } if ($command->isInverse()) { - return !$propModel->getProperty($command->getToggleProperty()); + return !$propModel->getProperty($toggleProperty); } - return (bool) $propModel->getProperty($command->getToggleProperty()); + return (bool) $propModel->getProperty($toggleProperty); } /** @@ -645,15 +695,10 @@ private function calculateHref(CommandInterface $command, $model) */ private function getCommandLabel(CommandInterface $command) { - if (null === $label = $command->getLabel()) { + if ('' === $label = $command->getLabel()) { $label = $command->getName(); } - if (\is_array($label)) { - return $this->translate($label[0]); - } - $label = $this->translate($label); - - return \is_array($label) ? $label[0] : $label; + return $this->translate($label); } } diff --git a/src/Contao/View/Contao2BackendView/ContaoBackendViewTemplate.php b/src/Contao/View/Contao2BackendView/ContaoBackendViewTemplate.php index 6f74ad22..59fe663d 100644 --- a/src/Contao/View/Contao2BackendView/ContaoBackendViewTemplate.php +++ b/src/Contao/View/Contao2BackendView/ContaoBackendViewTemplate.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -16,7 +16,8 @@ * @author David Molineus * @author Sven Baumann * @author Richard Henkenjohann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -27,21 +28,25 @@ use Contao\System; use ContaoCommunityAlliance\DcGeneral\Clipboard\Clipboard; use ContaoCommunityAlliance\DcGeneral\Contao\InputProvider; +use ContaoCommunityAlliance\DcGeneral\DataDefinitionContainerInterface; use ContaoCommunityAlliance\DcGeneral\DefaultEnvironment; use ContaoCommunityAlliance\DcGeneral\View\ViewTemplateInterface; use ContaoCommunityAlliance\Translator\TranslatorInterface; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; /** * This class is used for the contao backend view as template. + * + * @psalm-suppress PropertyNotSetInConstructor */ class ContaoBackendViewTemplate extends BackendTemplate implements ViewTemplateInterface, TranslatorInterface { /** * The translator. * - * @var TranslatorInterface + * @var TranslatorInterface|null */ - protected $translator; + protected $translator = null; /** * Get the translator. @@ -50,6 +55,9 @@ class ContaoBackendViewTemplate extends BackendTemplate implements ViewTemplateI */ public function getTranslator() { + if (null === $this->translator) { + throw new \RuntimeException('Translator not set.'); + } return $this->translator; } @@ -67,16 +75,6 @@ public function setTranslator(TranslatorInterface $translator) return $this; } - /** - * {@inheritDoc} - */ - public function setData($data) - { - parent::setData($data); - - return $this; - } - /** * {@inheritDoc} */ @@ -100,11 +98,7 @@ public function get($name) */ public function translate($string, $domain = null, array $parameters = [], $locale = null) { - if ($this->translator) { - return $this->translator->translate($string, $domain, $parameters, $locale); - } - - return $string; + return $this->getTranslator()->translate($string, $domain, $parameters, $locale); } /** @@ -112,11 +106,7 @@ public function translate($string, $domain = null, array $parameters = [], $loca */ public function translatePluralized($string, $number, $domain = null, array $parameters = [], $locale = null) { - if ($this->translator) { - return $this->translator->translatePluralized($string, $number, $domain, $parameters, $locale); - } - - return $string; + return $this->getTranslator()->translatePluralized($string, $number, $domain, $parameters, $locale); } /** @@ -124,17 +114,28 @@ public function translatePluralized($string, $number, $domain = null, array $par * * @return string * - * @SuppressWarnings(PHPMD.Superglobals) + * @deprecated Do not use */ public function getBackButton(): string { - $dataContainer = System::getContainer()->get('cca.dc-general.data-definition-container'); - $dataDefinition = $dataContainer->getDefinition($this->table); + $dataContainer = System::getContainer()->get('cca.dc-general.data-definition-container'); + assert($dataContainer instanceof DataDefinitionContainerInterface); + + /** @psalm-suppress UndefinedThisPropertyFetch */ + $table = $this->table; + assert(\is_string($table)); + $dataDefinition = $dataContainer->getDefinition($table); + + $translator = System::getContainer()->get('cca.translator.contao_translator'); + assert($translator instanceof TranslatorInterface); + + $dispatcher = System::getContainer()->get('event_dispatcher'); + assert($dispatcher instanceof EventDispatcherInterface); $environment = new DefaultEnvironment(); $environment->setDataDefinition($dataDefinition); - $environment->setTranslator(System::getContainer()->get('cca.translator.contao_translator')); - $environment->setEventDispatcher(System::getContainer()->get('event_dispatcher')); + $environment->setTranslator($translator); + $environment->setEventDispatcher($dispatcher); $environment->setInputProvider(new InputProvider()); $environment->setClipboard(new Clipboard()); @@ -150,6 +151,16 @@ public function getData() return parent::getData(); } + /** + * {@inheritDoc} + */ + public function setData($arrData) + { + parent::setData($arrData); + + return $this; + } + /** * {@inheritDoc} */ @@ -163,6 +174,7 @@ public function parse() */ public function output() { + /** @psalm-suppress DeprecatedMethod */ parent::output(); } // @codingStandardsIgnoreEnd diff --git a/src/Contao/View/Contao2BackendView/ContaoWidgetManager.php b/src/Contao/View/Contao2BackendView/ContaoWidgetManager.php index 3f9c39ce..12d3669c 100644 --- a/src/Contao/View/Contao2BackendView/ContaoWidgetManager.php +++ b/src/Contao/View/Contao2BackendView/ContaoWidgetManager.php @@ -37,13 +37,19 @@ use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\DecodePropertyValueForWidgetEvent; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\EncodePropertyValueFromWidgetEvent; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\ResolveWidgetErrorMessageEvent; +use ContaoCommunityAlliance\DcGeneral\Controller\ControllerInterface; use ContaoCommunityAlliance\DcGeneral\Data\ModelId; use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; use ContaoCommunityAlliance\DcGeneral\Data\PropertyValueBag; use ContaoCommunityAlliance\DcGeneral\Data\PropertyValueBagInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralInvalidArgumentException; use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralRuntimeException; +use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; +use ContaoCommunityAlliance\DcGeneral\SessionStorageInterface; +use ContaoCommunityAlliance\Translator\TranslatorInterface; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; /** * Class ContaoWidgetManager. @@ -60,6 +66,8 @@ class ContaoWidgetManager * The environment in use. * * @var ContaoFrameworkInterface + * + * @psalm-suppress DeprecatedInterface */ protected $framework; @@ -87,7 +95,10 @@ public function __construct(EnvironmentInterface $environment, ModelInterface $m { $this->environment = $environment; $this->model = $model; - $this->framework = System::getContainer()->get('contao.framework'); + $framework = System::getContainer()->get('contao.framework'); + assert($framework instanceof ContaoFrameworkInterface); + + $this->framework = $framework; } /** @@ -102,13 +113,15 @@ public function __construct(EnvironmentInterface $environment, ModelInterface $m public function encodeValue($property, $value, PropertyValueBag $propertyValues) { $environment = $this->getEnvironment(); + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); $event = new EncodePropertyValueFromWidgetEvent($environment, $this->model, $propertyValues); $event ->setProperty($property) ->setValue($value); - $environment->getEventDispatcher()->dispatch($event, EncodePropertyValueFromWidgetEvent::NAME); + $dispatcher->dispatch($event, EncodePropertyValueFromWidgetEvent::NAME); return $event->getValue(); } @@ -124,18 +137,20 @@ public function encodeValue($property, $value, PropertyValueBag $propertyValues) public function decodeValue($property, $value) { $environment = $this->getEnvironment(); + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); $event = new DecodePropertyValueForWidgetEvent($environment, $this->model); $event ->setProperty($property) ->setValue($value); - $environment->getEventDispatcher()->dispatch($event, DecodePropertyValueForWidgetEvent::NAME); + $dispatcher->dispatch($event, DecodePropertyValueForWidgetEvent::NAME); return $event->getValue(); } /** - * {@inheritdoc} + * @return EnvironmentInterface */ public function getEnvironment() { @@ -143,12 +158,14 @@ public function getEnvironment() } /** - * {@inheritDoc} + * @param string $property + * + * @return bool */ public function hasWidget($property) { try { - return null !== $this->getWidget($property); + return ($this->getWidget($property) instanceof Widget); // @codingStandardsIgnoreStart } catch (\Exception $e) { // Fall though and return false. @@ -167,10 +184,12 @@ public function hasWidget($property) */ public function loadRichTextEditor($buffer, Widget $widget) { + /** @psalm-suppress UndefinedMagicPropertyFetch */ + $rte = $widget->rte; if ( - (null === $widget->rte) - || ((0 !== (\strncmp($widget->rte, 'tiny', 4))) - && (0 !== \strncmp($widget->rte, 'ace', 3))) + (null === $rte) + || ((0 !== (\strncmp($rte, 'tiny', 4))) + && (0 !== \strncmp($rte, 'ace', 3))) ) { return $buffer; } @@ -178,7 +197,7 @@ public function loadRichTextEditor($buffer, Widget $widget) $backendAdapter = $this->framework->getAdapter(Backend::class); $templateLoader = $this->framework->getAdapter(TemplateLoader::class); - [$file, $type] = \explode('|', $widget->rte) + [null, null]; + [$file, $type] = \explode('|', $rte) + ['', '']; $templateName = 'be_' . $file; // This test if the rich text editor template exist. @@ -188,9 +207,9 @@ public function loadRichTextEditor($buffer, Widget $widget) $template ->set('selector', 'ctrl_' . $widget->id) ->set('type', $type) - ->set('readonly', (bool) $widget->readonly); + ->set('readonly', $widget->readonly); - if (0 !== \strncmp($widget->rte, 'tiny', 4)) { + if (0 !== \strncmp($rte, 'tiny', 4)) { /** @deprecated Deprecated since Contao 4.0, to be removed in Contao 5.0 */ $template->set('language', $backendAdapter->getTinyMceLanguage()); } @@ -210,7 +229,10 @@ public function loadRichTextEditor($buffer, Widget $widget) protected function getUniqueId($propertyName) { $inputProvider = $this->getEnvironment()->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + $sessionStorage = $this->getEnvironment()->getSessionStorage(); + assert($sessionStorage instanceof SessionStorageInterface); $selector = 'ctrl_' . $propertyName; @@ -237,21 +259,25 @@ protected function getUniqueId($propertyName) /** * Retrieve the instance of a widget for the given property. * - * @param string $property Name of the property for which the widget shall be retrieved. - * @param PropertyValueBag $inputValues The input values to use (optional). + * @param string $property Name of the property for which the widget shall be retrieved. + * @param PropertyValueBagInterface $inputValues The input values to use (optional). * - * @return Widget + * @return Widget|null * - * @throws DcGeneralRuntimeException When No widget could be build. + * @throws DcGeneralRuntimeException When No widget could be built. * @throws DcGeneralInvalidArgumentException When property is not defined in the property definitions. * * @SuppressWarnings(PHPMD.Superglobals) * @SuppressWarnings(PHPMD.CamelCaseVariableName) */ - public function getWidget($property, PropertyValueBag $inputValues = null) + public function getWidget($property, PropertyValueBagInterface $inputValues = null) { - $environment = $this->getEnvironment(); - $propertyDefinitions = $environment->getDataDefinition()->getPropertiesDefinition(); + $environment = $this->getEnvironment(); + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + + $propertyDefinitions = $definition->getPropertiesDefinition(); if (!$propertyDefinitions->hasProperty($property)) { throw new DcGeneralInvalidArgumentException( @@ -263,18 +289,19 @@ public function getWidget($property, PropertyValueBag $inputValues = null) $model->setId($this->model->getId()); if ($inputValues) { + $controller = $environment->getController(); + assert($controller instanceof ControllerInterface); + $values = new PropertyValueBag($inputValues->getArrayCopy()); - $this->environment->getController()->updateModelFromPropertyBag($model, $values); + $controller->updateModelFromPropertyBag($model, $values); } $event = new BuildWidgetEvent($environment, $model, $propertyDefinitions->getProperty($property)); - $environment->getEventDispatcher()->dispatch($event, $event::NAME); - if (!$event->getWidget()) { - throw new DcGeneralRuntimeException( - \sprintf('Widget was not build for property %s::%s.', $this->model->getProviderName(), $property) - ); - } + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $dispatcher->dispatch($event, $event::NAME); return $event->getWidget(); } @@ -292,7 +319,9 @@ public function getWidget($property, PropertyValueBag $inputValues = null) protected function buildDatePicker($objWidget) { $translator = $this->getEnvironment()->getTranslator(); - $strFormat = $GLOBALS['TL_CONFIG'][$objWidget->rgxp . 'Format']; + assert($translator instanceof TranslatorInterface); + + $strFormat = $GLOBALS['TL_CONFIG'][$objWidget->rgxp . 'Format']; switch ($objWidget->rgxp) { case 'datim': @@ -330,14 +359,15 @@ protected function buildDatePicker($objWidget) */ protected function generateHelpText($property) { - $propInfo = $this->getEnvironment()->getDataDefinition()->getPropertiesDefinition()->getProperty($property); + $definition = $this->getEnvironment()->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $propInfo = $definition->getPropertiesDefinition()->getProperty($property); $label = $propInfo->getDescription(); $widgetType = $propInfo->getWidgetType(); if ( - empty($label) - || ('password' === $widgetType) - || !\is_string($label) + ('password' === $widgetType) || !$GLOBALS['TL_CONFIG']['showHelp'] ) { return ''; @@ -349,31 +379,34 @@ protected function generateHelpText($property) /** * Render the widget for the named property. * - * @param string $property The name of the property for which the widget shall be rendered. - * @param bool $ignoreErrors Flag if the error property of the widget shall get cleared prior rendering. - * @param PropertyValueBag $inputValues The input values to use (optional). + * @param string $property The name of the property for which the widget shall be rendered. + * @param bool $ignoreErrors Flag if the error property of the widget shall get + * cleared prior rendering. + * @param PropertyValueBagInterface $inputValues The input values to use (optional). * * @return string * * @throws DcGeneralRuntimeException For unknown properties. */ - public function renderWidget($property, $ignoreErrors = false, PropertyValueBag $inputValues = null) + public function renderWidget($property, $ignoreErrors = false, PropertyValueBagInterface $inputValues = null) { /** @var Widget $widget */ $widget = $this->getWidget($property, $inputValues); - if (!$widget) { - throw new DcGeneralRuntimeException('No widget for property ' . $property); - } $this->cleanErrors($widget, $ignoreErrors); $this->widgetAddError($property, $widget, $inputValues, $ignoreErrors); - $propInfo = $this->getEnvironment()->getDataDefinition()->getPropertiesDefinition()->getProperty($property); + $definition = $this->getEnvironment()->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $propInfo = $definition->getPropertiesDefinition()->getProperty($property); + /** @psalm-suppress UndefinedMagicPropertyFetch */ $isHideInput = (bool) $widget->hideInput; $hiddenFields = ($isHideInput) ? $this->buildHiddenFields($widget->value, $widget->name) : null; + /** @psalm-suppress UndefinedMagicPropertyFetch */ $content = (new ContaoBackendViewTemplate('dcbe_general_field')) ->set('strName', $property) ->set('strClass', $widget->tl_class) @@ -406,7 +439,7 @@ public function renderWidget($property, $ignoreErrors = false, PropertyValueBag */ public function buildHiddenFields($value, string $propertyName): array { - if (is_string($value)) { + if (\is_string($value)) { return [$propertyName => $value]; } @@ -415,7 +448,7 @@ public function buildHiddenFields($value, string $propertyName): array $values[] = $this->buildHiddenFields($item, $propertyName . '[' . $key . ']'); } - return array_merge(...$values); + return \array_merge(...$values); } /** @@ -424,7 +457,7 @@ public function buildHiddenFields($value, string $propertyName): array * @SuppressWarnings(PHPMD.Superglobals) * @SuppressWarnings(PHPMD.CamelCaseVariableName) */ - public function processInput(PropertyValueBag $propertyValues) + public function processInput(PropertyValueBag $propertyValues): void { // @codingStandardsIgnoreStart - Remember current POST data and clear it. $post = $_POST; @@ -441,8 +474,10 @@ public function processInput(PropertyValueBag $propertyValues) foreach (\array_keys($propertyValues->getArrayCopy()) as $property) { // NOTE: the passed input values are RAW DATA from the input provider - aka widget known values and not // native data as in the model. - // Therefore we do not need to decode them but MUST encode them. + // Therefore, we do not need to decode them but MUST encode them. $widget = $this->getWidget($property, $propertyValues); + assert($widget instanceof Widget); + $widget->validate(); if ($widget->hasErrors()) { @@ -471,7 +506,7 @@ public function processInput(PropertyValueBag $propertyValues) /** * {@inheritDoc} */ - public function processErrors(PropertyValueBag $propertyValues) + public function processErrors(PropertyValueBag $propertyValues): void { $propertyErrors = $propertyValues->getInvalidPropertyErrors(); @@ -480,9 +515,11 @@ public function processErrors(PropertyValueBag $propertyValues) } $dispatcher = $this->getEnvironment()->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); foreach ($propertyErrors as $property => $errors) { $widget = $this->getWidget($property); + assert($widget instanceof Widget); foreach ($errors as $error) { $event = new ResolveWidgetErrorMessageEvent($this->getEnvironment(), $error); @@ -499,6 +536,8 @@ public function processErrors(PropertyValueBag $propertyValues) * @param bool $ignoreErrors The flag for errors cleared. * * @return void + * + * @throws \ReflectionException */ protected function cleanErrors(Widget $widget, $ignoreErrors = false) { diff --git a/src/Contao/View/Contao2BackendView/Controller/ClipboardController.php b/src/Contao/View/Contao2BackendView/Controller/ClipboardController.php index f93d1ddd..b393ee96 100644 --- a/src/Contao/View/Contao2BackendView/Controller/ClipboardController.php +++ b/src/Contao/View/Contao2BackendView/Controller/ClipboardController.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2021 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -16,7 +16,8 @@ * @author David Molineus * @author Stefan Heimes * @author Sven Baumann - * @copyright 2013-2021 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -26,6 +27,7 @@ use ContaoCommunityAlliance\Contao\Bindings\ContaoEvents; use ContaoCommunityAlliance\Contao\Bindings\Events\Backend\AddToUrlEvent; use ContaoCommunityAlliance\Contao\Bindings\Events\Controller\RedirectEvent; +use ContaoCommunityAlliance\DcGeneral\Clipboard\ClipboardInterface; use ContaoCommunityAlliance\DcGeneral\Clipboard\Filter; use ContaoCommunityAlliance\DcGeneral\Clipboard\Item; use ContaoCommunityAlliance\DcGeneral\Clipboard\ItemInterface; @@ -33,13 +35,18 @@ use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\ContaoBackendViewTemplate; use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminator; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\ViewHelpers; +use ContaoCommunityAlliance\DcGeneral\Data\DataProviderInterface; use ContaoCommunityAlliance\DcGeneral\Data\ModelId; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\DcGeneralEvents; use ContaoCommunityAlliance\DcGeneral\DcGeneralViews; use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; use ContaoCommunityAlliance\DcGeneral\Event\ActionEvent; use ContaoCommunityAlliance\DcGeneral\Event\FormatModelLabelEvent; use ContaoCommunityAlliance\DcGeneral\Event\ViewEvent; +use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; +use ContaoCommunityAlliance\Translator\TranslatorInterface; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\EventDispatcher\EventSubscriberInterface; /** @@ -123,8 +130,15 @@ private function checkPermission(ActionEvent $event) { $actionName = $event->getAction()->getName(); - $environment = $event->getEnvironment(); - $basicDefinition = $environment->getDataDefinition()->getBasicDefinition(); + $environment = $event->getEnvironment(); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + + $basicDefinition = $definition->getBasicDefinition(); if ( (('create' === $actionName) && (true === $basicDefinition->isCreatable())) @@ -137,11 +151,11 @@ private function checkPermission(ActionEvent $event) $permissionMessage = 'You have no permission for model ' . $actionName . ' '; switch ($actionName) { case 'create': - $permissionMessage .= 'in ' . $environment->getDataDefinition()->getName(); + $permissionMessage .= 'in ' . $definition->getName(); break; case 'cut': - $permissionMessage .= $environment->getInputProvider()->getParameter('source'); + $permissionMessage .= $inputProvider->getParameter('source'); break; default: @@ -167,10 +181,16 @@ private function checkPermission(ActionEvent $event) */ private function clearClipboard(ActionEvent $event, $redirect = true) { - $environment = $event->getEnvironment(); + $environment = $event->getEnvironment(); + $eventDispatcher = $environment->getEventDispatcher(); - $clipboard = $environment->getClipboard(); - $input = $environment->getInputProvider(); + assert($eventDispatcher instanceof EventDispatcherInterface); + + $clipboard = $environment->getClipboard(); + assert($clipboard instanceof ClipboardInterface); + + $input = $environment->getInputProvider(); + assert($input instanceof InputProviderInterface); if ($clipboardId = $input->getParameter('clipboard-item')) { $clipboard->removeByClipboardId($clipboardId); @@ -224,8 +244,13 @@ private function addToClipboard(ActionEvent $event) { $actionName = $event->getAction()->getName(); $environment = $event->getEnvironment(); - $input = $environment->getInputProvider(); - $clipboard = $environment->getClipboard(); + + $input = $environment->getInputProvider(); + assert($input instanceof InputProviderInterface); + + $clipboard = $environment->getClipboard(); + assert($clipboard instanceof ClipboardInterface); + $parentIdRaw = $input->getParameter('pid'); if ($parentIdRaw) { @@ -243,7 +268,12 @@ private function addToClipboard(ActionEvent $event) return; } - $providerName = $environment->getDataDefinition()->getBasicDefinition()->getDataProvider(); + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $providerName = $definition->getBasicDefinition()->getDataProvider(); + assert(\is_string($providerName)); + $item = new UnsavedItem($clipboardActionName, $parentId, $providerName); // Remove other create items, there can only be one create item in the clipboard or many others @@ -287,6 +317,7 @@ private function addToClipboard(ActionEvent $event) protected function isAddingAllowed(EnvironmentInterface $environment) { $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); // No manual sorting property defined, no need to add it to the clipboard. // Or we already have an after or into attribute, a handler can pick it up. @@ -313,23 +344,38 @@ public function handleView(ViewEvent $event) return; } - $environment = $event->getEnvironment(); - $input = $environment->getInputProvider(); + $environment = $event->getEnvironment(); + + $input = $environment->getInputProvider(); + assert($input instanceof InputProviderInterface); + $eventDispatcher = $environment->getEventDispatcher(); - $basicDefinition = $environment->getDataDefinition()->getBasicDefinition(); + assert($eventDispatcher instanceof EventDispatcherInterface); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $basicDefinition = $definition->getBasicDefinition(); + + $dataProvider = $basicDefinition->getDataProvider(); + assert(\is_string($dataProvider)); $filter = new Filter(); - $filter->andModelIsFromProvider($basicDefinition->getDataProvider()); + $filter->andModelIsFromProvider($dataProvider); if ($parentProviderName = $basicDefinition->getParentDataProvider()) { $filter->andParentIsFromProvider($parentProviderName); } else { $filter->andHasNoParent(); } + $clipboard = $environment->getClipboard(); + assert($clipboard instanceof ClipboardInterface); + $options = []; - foreach ($environment->getClipboard()->fetch($filter) as $item) { + foreach ($clipboard->fetch($filter) as $item) { $modelId = $item->getModelId(); $dataProvider = $environment->getDataProvider($item->getDataProviderName()); + assert($dataProvider instanceof DataProviderInterface); if ($modelId) { $config = $dataProvider->getEmptyConfig(); @@ -348,7 +394,11 @@ public function handleView(ViewEvent $event) $label = $label['content']; } else { $model = $dataProvider->getEmptyModel(); - $label = $environment->getTranslator()->translate('new.0', $item->getDataProviderName()); + + $translator = $environment->getTranslator(); + assert($translator instanceof TranslatorInterface); + + $label = $translator->translate('new.0', $item->getDataProviderName()); } $options[$item->getClipboardId()] = ['item' => $item, 'model' => $model, 'label' => $label]; @@ -385,13 +435,18 @@ public function handleView(ViewEvent $event) */ private function removeItemsFromClipboard(ActionEvent $event) { - $environment = $event->getEnvironment(); - if ('select' === $environment->getInputProvider()->getParameter('act')) { + $environment = $event->getEnvironment(); + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + + if ('select' === $inputProvider->getParameter('act')) { return; } $clipboard = $environment->getClipboard(); - $filter = new Filter(); + assert($clipboard instanceof ClipboardInterface); + + $filter = new Filter(); $filter->andActionIs(ItemInterface::CREATE); $items = $clipboard->fetch($filter); foreach ($items as $item) { diff --git a/src/Contao/View/Contao2BackendView/EditMask.php b/src/Contao/View/Contao2BackendView/EditMask.php index 2a4704f8..64cadd2e 100644 --- a/src/Contao/View/Contao2BackendView/EditMask.php +++ b/src/Contao/View/Contao2BackendView/EditMask.php @@ -26,6 +26,7 @@ namespace ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView; use Contao\BackendUser; +use Contao\CoreBundle\Intl\Locales; use Contao\Image; use Contao\System; use ContaoCommunityAlliance\Contao\Bindings\ContaoEvents; @@ -33,13 +34,18 @@ use ContaoCommunityAlliance\Contao\Bindings\Events\Controller\RedirectEvent; use ContaoCommunityAlliance\Contao\Bindings\Events\System\GetReferrerEvent; use ContaoCommunityAlliance\Contao\Bindings\Events\System\LogEvent; +use ContaoCommunityAlliance\DcGeneral\Clipboard\ClipboardInterface; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\GetEditMaskSubHeadlineEvent; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\GetEditModeButtonsEvent; +use ContaoCommunityAlliance\DcGeneral\Controller\ControllerInterface; +use ContaoCommunityAlliance\DcGeneral\Data\DataProviderInterface; use ContaoCommunityAlliance\DcGeneral\Data\DefaultEditInformation; +use ContaoCommunityAlliance\DcGeneral\Data\EditInformationInterface; use ContaoCommunityAlliance\DcGeneral\Data\ModelId; use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; use ContaoCommunityAlliance\DcGeneral\Data\MultiLanguageDataProviderInterface; use ContaoCommunityAlliance\DcGeneral\Data\PropertyValueBag; +use ContaoCommunityAlliance\DcGeneral\Data\PropertyValueBagInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\BasicDefinitionInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\PropertiesDefinitionInterface; @@ -53,6 +59,10 @@ use ContaoCommunityAlliance\DcGeneral\Event\PrePersistModelEvent; use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralInvalidArgumentException; use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralRuntimeException; +use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; +use ContaoCommunityAlliance\DcGeneral\SessionStorageInterface; +use ContaoCommunityAlliance\Translator\TranslatorInterface; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; /** * This class manages the displaying of the edit/create mask containing the widgets. @@ -61,6 +71,7 @@ * * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * @SuppressWarnings(PHPMD.ExcessiveClassLength) */ class EditMask { @@ -109,20 +120,20 @@ class EditMask /** * The default edit information. * - * @var DefaultEditInformation + * @var EditInformationInterface */ - private DefaultEditInformation $editInformation; + private EditInformationInterface $editInformation; /** * Create the edit mask. * - * @param BackendViewInterface $view The view in use. - * @param ModelInterface $model The model with the current data. - * @param ModelInterface $originalModel The data from the original data. - * @param callable $preFunction The function to call before saving an item. - * @param callable $postFunction The function to call after saving an item. - * @param string $breadcrumb The rendered breadcrumb. - * @param DefaultEditInformation $editInformation The default edit information. + * @param BackendViewInterface $view The view in use. + * @param ModelInterface $model The model with the current data. + * @param ModelInterface $originalModel The data from the original data. + * @param callable|null $preFunction The function to call before saving an item. + * @param callable|null $postFunction The function to call after saving an item. + * @param string $breadcrumb The rendered breadcrumb. + * @param EditInformationInterface|null $editInformation The default edit information. */ public function __construct( $view, @@ -131,9 +142,12 @@ public function __construct( $preFunction, $postFunction, $breadcrumb, - ?DefaultEditInformation $editInformation = null + ?EditInformationInterface $editInformation = null ) { - $this->environment = $view->getEnvironment(); + if (null === $environment = $view->getEnvironment()) { + throw new \InvalidArgumentException('View has no environment'); + } + $this->environment = $environment; $this->model = $model; $this->originalModel = $originalModel; $this->preFunction = $preFunction; @@ -148,6 +162,7 @@ public function __construct( ); // @codingStandardsIgnoreEnd $editInformation = System::getContainer()->get('cca.dc-general.edit-information'); + assert($editInformation instanceof EditInformationInterface); } $this->editInformation = $editInformation; @@ -170,7 +185,10 @@ public function getEnvironment() */ protected function getDataDefinition() { - return $this->getEnvironment()->getDataDefinition(); + $definition = $this->getEnvironment()->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + return $definition; } /** @@ -180,7 +198,10 @@ protected function getDataDefinition() */ protected function isPopup() { - return $this->getEnvironment()->getInputProvider()->getParameter('popup'); + $inputProvider = $this->getEnvironment()->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + + return $inputProvider->getParameter('popup'); } /** @@ -200,7 +221,11 @@ protected function checkEditable($model) // Check if table is editable. if ($model->getId() && !$definition->getBasicDefinition()->isEditable()) { $message = 'DataContainer ' . $definition->getName() . ' is not editable'; - $environment->getEventDispatcher()->dispatch( + + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $dispatcher->dispatch( new LogEvent($message, 'ERROR', 'DC_General - edit()'), ContaoEvents::SYSTEM_LOG ); @@ -225,7 +250,11 @@ protected function checkCreatable($model) // Check if table is closed, but we are adding a new item. if (!($model->getId() || $definition->getBasicDefinition()->isCreatable())) { $message = 'DataContainer ' . $definition->getName() . ' is closed'; - $environment->getEventDispatcher()->dispatch( + + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $dispatcher->dispatch( new LogEvent($message, 'ERROR', 'DC_General - edit()'), ContaoEvents::SYSTEM_LOG ); @@ -265,6 +294,7 @@ protected function ensurePropertyExists($property, $propertyDefinitions) protected function processInput($widgetManager) { $input = $this->getEnvironment()->getInputProvider(); + assert($input instanceof InputProviderInterface); if ($input->getValue('FORM_SUBMIT') === $this->getDataDefinition()->getName()) { $propertyValues = new PropertyValueBag(); @@ -295,11 +325,15 @@ protected function processInput($widgetManager) protected function handlePrePersist() { $environment = $this->getEnvironment(); + if (null !== $this->preFunction) { \call_user_func($this->preFunction, $environment, $this->model, $this->originalModel); } - $environment->getEventDispatcher()->dispatch( + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $dispatcher->dispatch( new PrePersistModelEvent($environment, $this->model, $this->originalModel), PrePersistModelEvent::NAME, ); @@ -313,12 +347,16 @@ protected function handlePrePersist() protected function handlePostPersist() { $environment = $this->getEnvironment(); + if (null !== $this->postFunction) { \call_user_func($this->postFunction, $environment, $this->model, $this->originalModel); } + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + $event = new PostPersistModelEvent($environment, $this->model, $this->originalModel); - $environment->getEventDispatcher()->dispatch($event, $event::NAME); + $dispatcher->dispatch($event, $event::NAME); } /** @@ -336,6 +374,8 @@ protected function handlePostPersist() protected function getButtonLabel($buttonLabel) { $translator = $this->getEnvironment()->getTranslator(); + assert($translator instanceof TranslatorInterface); + if (($label = $translator->translate($buttonLabel, $this->getDataDefinition()->getName())) !== $buttonLabel) { return $label; } @@ -358,6 +398,9 @@ protected function getButtonLabel($buttonLabel) */ protected function getEditButtons() { + $inputProvider = $this->getEnvironment()->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + $buttons = []; $basicDefinition = $this->getDataDefinition()->getBasicDefinition(); @@ -377,7 +420,7 @@ protected function getEditButtons() ); $buttons['save'] = $buttonTemplate->parse(); - if (!$this->getEnvironment()->getInputProvider()->getParameter('nb')) { + if (!$inputProvider->getParameter('nb')) { $buttonTemplate->setData( [ 'label' => $this->getButtonLabel('saveNclose'), @@ -395,7 +438,7 @@ protected function getEditButtons() if ( $basicDefinition->isCreatable() - && !$this->getEnvironment()->getInputProvider()->getParameter('nc') + && !$inputProvider->getParameter('nc') ) { $buttonTemplate->setData( [ @@ -412,7 +455,7 @@ protected function getEditButtons() $buttons['saveNcreate'] = $buttonTemplate->parse(); } - if ($this->getEnvironment()->getInputProvider()->hasParameter('s2e')) { + if ($inputProvider->hasParameter('s2e')) { $buttonTemplate->setData( [ 'label' => $this->getButtonLabel('saveNedit'), @@ -452,7 +495,10 @@ protected function getEditButtons() $event = new GetEditModeButtonsEvent($this->getEnvironment()); $event->setButtons($buttons); - $this->getEnvironment()->getEventDispatcher()->dispatch($event, $event::NAME); + $dispatcher = $this->getEnvironment()->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $dispatcher->dispatch($event, $event::NAME); $submitButtons = ['toggleIcon' => Image::getHtml('navcol.svg')]; $editButtons = $event->getButtons(); @@ -474,9 +520,9 @@ protected function getEditButtons() /** * Build the field sets. * - * @param ContaoWidgetManager $widgetManager The widget manager in use. - * @param PaletteInterface $palette The palette to use. - * @param PropertyValueBag $propertyValues The property values. + * @param ContaoWidgetManager $widgetManager The widget manager in use. + * @param PaletteInterface $palette The palette to use. + * @param PropertyValueBag|null $propertyValues The property values. * * @return array * @@ -485,13 +531,21 @@ protected function getEditButtons() */ protected function buildFieldSet($widgetManager, $palette, $propertyValues) { - $environment = $this->getEnvironment(); - $definition = $this->getDataDefinition(); - $translator = $environment->getTranslator(); + $environment = $this->getEnvironment(); + $definition = $this->getDataDefinition(); + + $translator = $environment->getTranslator(); + assert($translator instanceof TranslatorInterface); + + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + $propertyDefinitions = $definition->getPropertiesDefinition(); - $isAutoSubmit = ('auto' === $environment->getInputProvider()->getValue('SUBMIT_TYPE')); + $isAutoSubmit = ('auto' === $inputProvider->getValue('SUBMIT_TYPE')); $legendStates = $this->getLegendStates(); - $editInformation = System::getContainer()->get('cca.dc-general.edit-information'); + + $editInformation = System::getContainer()->get('cca.dc-general.edit-information'); + assert($editInformation instanceof EditInformationInterface); $fieldSets = []; $first = true; @@ -501,6 +555,7 @@ protected function buildFieldSet($widgetManager, $palette, $propertyValues) $definition->getName() ); $fields = []; + $fieldSet = []; $properties = $legend->getProperties($this->model, $propertyValues); if (!$properties) { @@ -534,7 +589,7 @@ protected function buildFieldSet($widgetManager, $palette, $propertyValues) if (!$property->isEditable($this->model, $propertyValues, $legend)) { $propertyDefinition = $propertyDefinitions->getProperty($property->getName()); $propertyDefinition->setExtra( - array_merge(($propertyDefinition->getExtra() ?? []), ['readonly' => true]) + \array_merge(($propertyDefinition->getExtra()), ['readonly' => true]) ); } @@ -571,10 +626,13 @@ protected function buildFieldSet($widgetManager, $palette, $propertyValues) */ protected function storeVersion(ModelInterface $model) { - $modelId = $model->getId(); - $environment = $this->getEnvironment(); - $definition = $this->getDataDefinition(); - $dataProvider = $environment->getDataProvider($model->getProviderName()); + $modelId = $model->getId(); + $environment = $this->getEnvironment(); + $definition = $this->getDataDefinition(); + + $dataProvider = $environment->getDataProvider($model->getProviderName()); + assert($dataProvider instanceof DataProviderInterface); + $dataProviderDefinition = $definition->getDataProviderDefinition(); $dataProviderInformation = $dataProviderDefinition->getInformation($model->getProviderName()); @@ -584,13 +642,18 @@ protected function storeVersion(ModelInterface $model) // Compare version and current record. $currentVersion = $dataProvider->getActiveVersion($modelId); + $model = $dataProvider->getVersion($modelId, $currentVersion); + assert($model instanceof ModelInterface); if ( !$currentVersion - || !$dataProvider->sameModels($model, $dataProvider->getVersion($modelId, $currentVersion)) + || !$dataProvider->sameModels($model, $model) ) { $user = BackendUser::getInstance(); - $dataProvider->saveVersion($model, $user->username); + $username = $user->username; + assert(\is_string($username)); + + $dataProvider->saveVersion($model, $username); } } @@ -618,9 +681,13 @@ protected function getManualSortingProperty() */ protected function handleSubmit(ModelInterface $model) { - $environment = $this->getEnvironment(); - $dispatcher = $environment->getEventDispatcher(); + $environment = $this->getEnvironment(); + + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); if ($inputProvider->hasValue('save')) { $newUrlEvent = new AddToUrlEvent('act=edit&btn=s&id=' . ModelId::fromModel($model)->getSerialized()); @@ -645,7 +712,10 @@ protected function handleSubmit(ModelInterface $model) } elseif ($inputProvider->hasValue('saveNback')) { $this->clearBackendStates(); - $parentProviderName = $environment->getDataDefinition()->getBasicDefinition()->getParentDataProvider(); + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $parentProviderName = $definition->getBasicDefinition()->getParentDataProvider(); $newUrlEvent = new GetReferrerEvent(false, $parentProviderName); $dispatcher->dispatch($newUrlEvent, ContaoEvents::SYSTEM_GET_REFERRER); @@ -656,7 +726,7 @@ protected function handleSubmit(ModelInterface $model) /** * Determine the headline to use. * - * @return string. + * @return string * * @deprecated This is deprecated since 2.3 and will be removed in 3.0. */ @@ -672,7 +742,7 @@ protected function getHeadline(): string /** * Determine the headline to use. * - * @return string. + * @return string */ protected function getSubHeadline(): string { @@ -680,9 +750,12 @@ protected function getSubHeadline(): string $event = new GetEditMaskSubHeadlineEvent($this->environment, $this->model); - $environment->getEventDispatcher()->dispatch($event, $event::NAME); + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $dispatcher->dispatch($event, $event::NAME); - return $event->getHeadline(); + return (string) $event->getHeadline(); } /** @@ -692,12 +765,17 @@ protected function getSubHeadline(): string * * @SuppressWarnings(PHPMD.Superglobals) * @SuppressWarnings(PHPMD.CyclomaticComplexity) + * @SuppressWarnings(PHPMD.LongVariable) */ protected function doPersist() { - $environment = $this->getEnvironment(); - $dataProvider = $environment->getDataProvider($this->model->getProviderName()); + $environment = $this->getEnvironment(); + + $dataProvider = $environment->getDataProvider($this->model->getProviderName()); + assert($dataProvider instanceof DataProviderInterface); + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); if (!$this->model->getMeta(ModelInterface::IS_CHANGED)) { return true; @@ -714,38 +792,49 @@ protected function doPersist() $models->push($this->model); $controller = $environment->getController(); + assert($controller instanceof ControllerInterface); + + $manualSortingProperty = $this->getManualSortingProperty(); + assert(\is_string($manualSortingProperty)); if ($inputProvider->hasParameter('after')) { $after = ModelId::fromSerialized($inputProvider->getParameter('after')); $previousDataProvider = $environment->getDataProvider($after->getDataProviderName()); + assert($previousDataProvider instanceof DataProviderInterface); + $previousFetchConfig = $previousDataProvider->getEmptyConfig(); $previousFetchConfig->setId($after->getId()); $previousModel = $previousDataProvider->fetch($previousFetchConfig); if ($previousModel) { - $controller->pasteAfter($previousModel, $models, $this->getManualSortingProperty()); + $controller->pasteAfter($previousModel, $models, $manualSortingProperty); } else { - $controller->pasteTop($models, $this->getManualSortingProperty()); + $controller->pasteTop($models, $manualSortingProperty); } } elseif ($inputProvider->hasParameter('into')) { $into = ModelId::fromSerialized($inputProvider->getParameter('into')); $parentDataProvider = $environment->getDataProvider($into->getDataProviderName()); + assert($parentDataProvider instanceof DataProviderInterface); + $parentFetchConfig = $parentDataProvider->getEmptyConfig(); $parentFetchConfig->setId($into->getId()); $parentModel = $parentDataProvider->fetch($parentFetchConfig); if ($parentModel) { - $controller->pasteInto($parentModel, $models, $this->getManualSortingProperty()); + $controller->pasteInto($parentModel, $models, $manualSortingProperty); } else { - $controller->pasteTop($models, $this->getManualSortingProperty()); + $controller->pasteTop($models, $manualSortingProperty); } } else { - $controller->pasteTop($models, $this->getManualSortingProperty()); + $controller->pasteTop($models, $manualSortingProperty); } - $environment->getClipboard()->clear()->saveTo($environment); + $clipboard = $environment->getClipboard(); + assert($clipboard instanceof ClipboardInterface); + + $clipboard->clear()->saveTo($environment); } else { if (!$this->allValuesUnique()) { return false; @@ -772,10 +861,16 @@ protected function doPersist() protected function allValuesUnique() { // Init some vars. - $environment = $this->getEnvironment(); - $translator = $environment->getTranslator(); - $dataProvider = $environment->getDataProvider($this->model->getProviderName()); + $environment = $this->getEnvironment(); + + $translator = $environment->getTranslator(); + assert($translator instanceof TranslatorInterface); + + $dataProvider = $environment->getDataProvider($this->model->getProviderName()); + assert($dataProvider instanceof DataProviderInterface); + $editInformation = System::getContainer()->get('cca.dc-general.edit-information'); + assert($editInformation instanceof EditInformationInterface); // Run each and check the unique flag. foreach ($this->getDataDefinition()->getPropertiesDefinition()->getPropertyNames() as $propertyName) { @@ -812,77 +907,102 @@ protected function allValuesUnique() * * @SuppressWarnings(PHPMD.LongVariable) * @SuppressWarnings(PHPMD.Superglobals) + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ public function execute() { - $environment = $this->getEnvironment(); - $definition = $this->getDataDefinition(); - $dataProvider = $environment->getDataProvider($this->model->getProviderName()); + $environment = $this->getEnvironment(); + $definition = $this->getDataDefinition(); + + $controller = $environment->getController(); + assert($controller instanceof ControllerInterface); + + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $model = $this->model; + assert($model instanceof ModelInterface); + + $dataProvider = $environment->getDataProvider($model->getProviderName()); + assert($dataProvider instanceof DataProviderInterface); + $dataProviderDefinition = $definition->getDataProviderDefinition(); - $dataProviderInformation = $dataProviderDefinition->getInformation($this->model->getProviderName()); - $inputProvider = $environment->getInputProvider(); - $palettesDefinition = $definition->getPalettesDefinition(); - $submitted = ($definition->getName() === $inputProvider->getValue('FORM_SUBMIT')); - $isAutoSubmit = ('auto' === $inputProvider->getValue('SUBMIT_TYPE')); - $editInformation = System::getContainer()->get('cca.dc-general.edit-information'); + $dataProviderInformation = $dataProviderDefinition->getInformation($model->getProviderName()); + + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + + $palettesDefinition = $definition->getPalettesDefinition(); + $submitted = ($definition->getName() === $inputProvider->getValue('FORM_SUBMIT')); + $isAutoSubmit = ('auto' === $inputProvider->getValue('SUBMIT_TYPE')); - $widgetManager = new ContaoWidgetManager($environment, $this->model); + $editInformation = System::getContainer()->get('cca.dc-general.edit-information'); + assert($editInformation instanceof EditInformationInterface); - $this->checkEditable($this->model); - $this->checkCreatable($this->model); + $widgetManager = new ContaoWidgetManager($environment, $model); - $environment->getEventDispatcher()->dispatch( - new PreEditModelEvent($environment, $this->model), + $this->checkEditable($model); + $this->checkCreatable($model); + + $dispatcher->dispatch( + new PreEditModelEvent($environment, $model), PreEditModelEvent::NAME ); - $environment->getEventDispatcher()->dispatch( - new EnforceModelRelationshipEvent($this->getEnvironment(), $this->model), + $dispatcher->dispatch( + new EnforceModelRelationshipEvent($this->getEnvironment(), $model), DcGeneralEvents::ENFORCE_MODEL_RELATIONSHIP ); // Pass 1: Get the palette for the values stored in the model. - $palette = $palettesDefinition->findPalette($this->model); + $palette = $palettesDefinition->findPalette($model); // Check if input mask has visible properties. - if (!\count($palette->getProperties($this->model))) { + if (!\count($palette->getProperties($model))) { // @codingStandardsIgnoreStart \trigger_error('No visible properties for this edit mask defined!', E_USER_ERROR); // @codingStandardsIgnoreEnd } $propertyValues = $this->processInput($widgetManager); + if ($submitted && $propertyValues) { // Pass 2: Determine the real palette we want to work on if we have some data submitted. - $palette = $palettesDefinition->findPalette($this->model, $propertyValues); + $palette = $palettesDefinition->findPalette($model, $propertyValues); // Update the model - the model might add some more errors to the propertyValueBag via exceptions. - $this->getEnvironment()->getController()->updateModelFromPropertyBag($this->model, $propertyValues); + $controller->updateModelFromPropertyBag($model, $propertyValues); } $fieldSets = $this->buildFieldSet($widgetManager, $palette, $propertyValues); - if ((!$isAutoSubmit) && $submitted && !$editInformation->getModelError($this->model)) { + if ((!$isAutoSubmit) && $submitted && !$editInformation->getModelError($model)) { if ($this->doPersist()) { - $this->handleSubmit($this->model); + $this->handleSubmit($model); } } + $errors = null; + if ($editInformation instanceof DefaultEditInformation) { + $errors = $editInformation->getFlatModelErrors($model); + } + $viewTemplate = new ContaoBackendViewTemplate('dcbe_general_edit'); $viewTemplate->setData( [ 'fieldsets' => $fieldSets, 'versions' => $dataProviderInformation->isVersioningEnabled() ? $dataProvider->getVersions( - $this->model->getId() + $model->getId() ) : null, 'subHeadline' => $this->getSubHeadline(), 'table' => $definition->getName(), 'enctype' => 'multipart/form-data', - 'error' => $editInformation->getFlatModelErrors($this->model), + 'error' => $errors, 'editButtons' => $this->getEditButtons(), 'noReload' => $editInformation->hasAnyModelError(), 'breadcrumb' => $this->breadcrumb, - 'model' => $this->model + 'model' => $model ] ); @@ -900,26 +1020,35 @@ public function execute() */ private function executeMultiLanguage(ContaoBackendViewTemplate $template) { + $dataProvider = $this->getEnvironment()->getDataProvider($this->model->getProviderName()); + assert($dataProvider instanceof DataProviderInterface); + if ( \in_array( MultiLanguageDataProviderInterface::class, - \class_implements( - $this->getEnvironment()->getDataProvider( - $this->model->getProviderName() - ) - ) + \class_implements($dataProvider) ) ) { /** @var MultiLanguageDataProviderInterface $dataProvider */ $dataProvider = $this->getEnvironment()->getDataProvider(); - $languages = System::getLanguages(); + + $locales = System::getContainer()->get('contao.intl.locales'); + assert($locales instanceof Locales); + + $languages = $locales->getLocales(null, true); + + $controller = $this->environment->getController(); + assert($controller instanceof ControllerInterface); + + $translator = $this->environment->getTranslator(); + assert($translator instanceof TranslatorInterface); $template->set( 'languages', - $this->getEnvironment()->getController()->getSupportedLanguages($this->model->getId()) + $controller->getSupportedLanguages($this->model->getId()) ) ->set('language', $dataProvider->getCurrentLanguage()) - ->set('languageSubmit', $this->environment->getTranslator()->translate('MSC.showSelected')) + ->set('languageSubmit', $translator->translate('MSC.showSelected')) ->set('languageHeadline', $languages[$dataProvider->getCurrentLanguage()] ?? ''); return; @@ -940,7 +1069,7 @@ private function executeMultiLanguage(ContaoBackendViewTemplate $template) */ protected function clearBackendStates() { - \setcookie('BE_PAGE_OFFSET', 0, 0, '/'); + \setcookie('BE_PAGE_OFFSET', '', 0, '/'); $_SESSION['TL_INFO'] = []; $_SESSION['TL_ERROR'] = []; @@ -971,19 +1100,21 @@ private function isLegendVisible($legend, $legendStates) */ private function getLegendStates() { - $environment = $this->getEnvironment(); - $definition = $environment->getDataDefinition(); - $legendStates = $environment->getSessionStorage()->get('LEGENDS') ?: []; + $environment = $this->getEnvironment(); - if (\array_key_exists($definition->getName(), $legendStates)) { - $legendStates = $legendStates[$definition->getName()]; + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); - return $legendStates; - } + $sessionStorage = $environment->getSessionStorage(); + assert($sessionStorage instanceof SessionStorageInterface); + + $legendStates = $sessionStorage->get('LEGENDS') ?: []; - $legendStates = []; + if (\array_key_exists($definition->getName(), $legendStates)) { + return $legendStates[$definition->getName()]; + } - return $legendStates; + return []; } /** diff --git a/src/Contao/View/Contao2BackendView/Event/BaseButtonEvent.php b/src/Contao/View/Contao2BackendView/Event/BaseButtonEvent.php index b96695ea..50962ccf 100644 --- a/src/Contao/View/Contao2BackendView/Event/BaseButtonEvent.php +++ b/src/Contao/View/Contao2BackendView/Event/BaseButtonEvent.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -32,6 +33,8 @@ class BaseButtonEvent extends AbstractEnvironmentAwareEvent { /** * The name of the event. + * + * @var string */ public const NAME = 'dc-general.view.contao2backend.button'; @@ -40,35 +43,35 @@ class BaseButtonEvent extends AbstractEnvironmentAwareEvent * * @var string */ - protected $attributes; + protected $attributes = ''; /** * The Html code to use for this button. * - * @var string + * @var string|null */ - protected $html; + protected $html = null; /** * The key/name of the button. * * @var string */ - protected $key; + protected $key = ''; /** * The label to use for the button. * * @var string */ - protected $label; + protected $label = ''; /** * The title to use for the button. * * @var string */ - protected $title; + protected $title = ''; /** * Set the HTML attributes for the button. @@ -113,7 +116,7 @@ public function setHtml($html) /** * Get the HTML code for the button. * - * @return string + * @return string|null */ public function getHtml() { diff --git a/src/Contao/View/Contao2BackendView/Event/BaseGetButtonsEvent.php b/src/Contao/View/Contao2BackendView/Event/BaseGetButtonsEvent.php index 42f54c8b..7c4d7cca 100644 --- a/src/Contao/View/Contao2BackendView/Event/BaseGetButtonsEvent.php +++ b/src/Contao/View/Contao2BackendView/Event/BaseGetButtonsEvent.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -33,6 +34,8 @@ class BaseGetButtonsEvent extends AbstractEnvironmentAwareEvent { /** * The name of the event. + * + * @var string */ public const NAME = 'dc-general.view.contao2backend.get-buttons'; @@ -41,7 +44,7 @@ class BaseGetButtonsEvent extends AbstractEnvironmentAwareEvent * * @var string[] */ - protected $buttons; + protected $buttons = []; /** * Set the list of buttons. diff --git a/src/Contao/View/Contao2BackendView/Event/BuildWidgetEvent.php b/src/Contao/View/Contao2BackendView/Event/BuildWidgetEvent.php index d28e7bc8..b4097c5d 100644 --- a/src/Contao/View/Contao2BackendView/Event/BuildWidgetEvent.php +++ b/src/Contao/View/Contao2BackendView/Event/BuildWidgetEvent.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author David Molineus * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -49,9 +50,9 @@ class BuildWidgetEvent extends AbstractModelAwareEvent /** * The instantiated widget. * - * @var Widget + * @var Widget|null */ - protected $widget; + protected $widget = null; /** * Create a new event. @@ -87,7 +88,7 @@ public function setWidget($widget) /** * Retrieve the widget instance from the event. * - * @return Widget + * @return Widget|null */ public function getWidget() { diff --git a/src/Contao/View/Contao2BackendView/Event/DecodePropertyValueForWidgetEvent.php b/src/Contao/View/Contao2BackendView/Event/DecodePropertyValueForWidgetEvent.php index 861d1ce1..4e579940 100644 --- a/src/Contao/View/Contao2BackendView/Event/DecodePropertyValueForWidgetEvent.php +++ b/src/Contao/View/Contao2BackendView/Event/DecodePropertyValueForWidgetEvent.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,8 @@ * @package contao-community-alliance/dc-general * @author Christian Schiffler * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -38,7 +39,7 @@ class DecodePropertyValueForWidgetEvent extends AbstractModelAwareEvent * * @var string */ - protected $property; + protected $property = ''; /** * The value of the data. @@ -58,10 +59,6 @@ class DecodePropertyValueForWidgetEvent extends AbstractModelAwareEvent */ public function setProperty($property) { - if (!\is_string($property)) { - throw new DcGeneralInvalidArgumentException('Only property name as string allowed.'); - } - $this->property = $property; return $this; diff --git a/src/Contao/View/Contao2BackendView/Event/EncodePropertyValueFromWidgetEvent.php b/src/Contao/View/Contao2BackendView/Event/EncodePropertyValueFromWidgetEvent.php index 217f6d47..e320471b 100644 --- a/src/Contao/View/Contao2BackendView/Event/EncodePropertyValueFromWidgetEvent.php +++ b/src/Contao/View/Contao2BackendView/Event/EncodePropertyValueFromWidgetEvent.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,8 @@ * @package contao-community-alliance/dc-general * @author Christian Schiffler * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -40,7 +41,7 @@ class EncodePropertyValueFromWidgetEvent extends AbstractModelAwareEvent * * @var string */ - protected $property; + protected $property = ''; /** * The value of the data. diff --git a/src/Contao/View/Contao2BackendView/Event/GetBreadcrumbEvent.php b/src/Contao/View/Contao2BackendView/Event/GetBreadcrumbEvent.php index 794aff5b..7b06d188 100644 --- a/src/Contao/View/Contao2BackendView/Event/GetBreadcrumbEvent.php +++ b/src/Contao/View/Contao2BackendView/Event/GetBreadcrumbEvent.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -37,7 +38,7 @@ class GetBreadcrumbEvent extends AbstractEnvironmentAwareEvent * * @var array */ - protected $elements; + protected $elements = []; /** * Set the breadcrumb elements to be displayed in the backend. diff --git a/src/Contao/View/Contao2BackendView/Event/GetGlobalButtonEvent.php b/src/Contao/View/Contao2BackendView/Event/GetGlobalButtonEvent.php index cb5e8a91..7a7c8015 100644 --- a/src/Contao/View/Contao2BackendView/Event/GetGlobalButtonEvent.php +++ b/src/Contao/View/Contao2BackendView/Event/GetGlobalButtonEvent.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -37,21 +38,21 @@ class GetGlobalButtonEvent extends BaseButtonEvent * * @var string */ - protected $accessKey; + protected $accessKey = ''; /** * The css class to use. * * @var string */ - protected $class; + protected $class = ''; /** * The href to use. * * @var string */ - protected $href; + protected $href = ''; /** * Set the hotkey for the button. diff --git a/src/Contao/View/Contao2BackendView/Event/GetGroupHeaderEvent.php b/src/Contao/View/Contao2BackendView/Event/GetGroupHeaderEvent.php index d35a8578..933cc58d 100644 --- a/src/Contao/View/Contao2BackendView/Event/GetGroupHeaderEvent.php +++ b/src/Contao/View/Contao2BackendView/Event/GetGroupHeaderEvent.php @@ -65,7 +65,7 @@ class GetGroupHeaderEvent extends AbstractModelAwareEvent /** * The value to be rendered. * - * @var string + * @var string|null */ protected $value; @@ -75,7 +75,7 @@ class GetGroupHeaderEvent extends AbstractModelAwareEvent * @param EnvironmentInterface $environment The environment. * @param ModelInterface $model The model being used as group header. * @param string $propertyName The name of the property being rendered into the group header. - * @param mixed $propertyValue The value of the property being rendered into the group header. + * @param string|null $propertyValue The value of the property being rendered into the group header. * @param string $groupingMode The grouping mode currently active. * @param int $groupingLength The grouping length currently active. */ @@ -146,7 +146,7 @@ public function setValue($value) /** * Retrieve the value to use in the group header. * - * @return string + * @return string|null */ public function getValue() { diff --git a/src/Contao/View/Contao2BackendView/Event/GetOperationButtonEvent.php b/src/Contao/View/Contao2BackendView/Event/GetOperationButtonEvent.php index a356f19e..59e5d6ab 100644 --- a/src/Contao/View/Contao2BackendView/Event/GetOperationButtonEvent.php +++ b/src/Contao/View/Contao2BackendView/Event/GetOperationButtonEvent.php @@ -171,7 +171,7 @@ public function isCircularReference() /** * Set the next model in the list, succeeding the current model. * - * @param ModelInterface $next The successor. + * @param ModelInterface|null $next The successor. * * @return $this */ @@ -195,7 +195,7 @@ public function getNext() /** * Set the previous model in the list, preceding the current model. * - * @param ModelInterface $previous The id of the predecessor. + * @param ModelInterface|null $previous The id of the predecessor. * * @return $this */ diff --git a/src/Contao/View/Contao2BackendView/Event/GetParentHeaderEvent.php b/src/Contao/View/Contao2BackendView/Event/GetParentHeaderEvent.php index 24a68a78..defe94ef 100644 --- a/src/Contao/View/Contao2BackendView/Event/GetParentHeaderEvent.php +++ b/src/Contao/View/Contao2BackendView/Event/GetParentHeaderEvent.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -15,7 +15,8 @@ * @author Tristan Lins * @author David Molineus * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -38,7 +39,7 @@ class GetParentHeaderEvent extends AbstractModelAwareEvent * * @var array */ - protected $additional; + protected $additional = []; /** * Set the additional lines that shall be added to the header section. diff --git a/src/Contao/View/Contao2BackendView/Event/GetPasteButtonEvent.php b/src/Contao/View/Contao2BackendView/Event/GetPasteButtonEvent.php index c0e7a3c4..dd9858e5 100644 --- a/src/Contao/View/Contao2BackendView/Event/GetPasteButtonEvent.php +++ b/src/Contao/View/Contao2BackendView/Event/GetPasteButtonEvent.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -122,7 +123,7 @@ class GetPasteButtonEvent extends BaseButtonEvent */ public function setCircularReference($circularReference) { - $this->circularReference = (bool) $circularReference; + $this->circularReference = $circularReference; return $this; } diff --git a/src/Contao/View/Contao2BackendView/Event/GetPasteRootButtonEvent.php b/src/Contao/View/Contao2BackendView/Event/GetPasteRootButtonEvent.php index dc2b93b3..fefdde37 100644 --- a/src/Contao/View/Contao2BackendView/Event/GetPasteRootButtonEvent.php +++ b/src/Contao/View/Contao2BackendView/Event/GetPasteRootButtonEvent.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -35,14 +36,14 @@ class GetPasteRootButtonEvent extends BaseButtonEvent * * @var string */ - protected $href; + protected $href = ''; /** * Determinator if the paste button shall be disabled. * * @var bool */ - protected $pasteDisabled; + protected $pasteDisabled = false; /** * Set the href for the button. diff --git a/src/Contao/View/Contao2BackendView/Event/ModelToLabelEvent.php b/src/Contao/View/Contao2BackendView/Event/ModelToLabelEvent.php index c8c110dc..e860b027 100644 --- a/src/Contao/View/Contao2BackendView/Event/ModelToLabelEvent.php +++ b/src/Contao/View/Contao2BackendView/Event/ModelToLabelEvent.php @@ -46,20 +46,22 @@ class ModelToLabelEvent extends AbstractModelAwareEvent * The label information instance. * * @var ModelFormatterConfigInterface + * + * @psalm-suppress PropertyNotSetInConstructor */ protected $listLabel; /** * The arguments to use when building the label from the format string. * - * @var array + * @var array */ protected $args = []; /** * Set the arguments to use when generating the final string representation using the format string. * - * @param array $args The arguments. + * @param array $args The arguments. * * @return ModelToLabelEvent */ @@ -73,7 +75,7 @@ public function setArgs($args) /** * Retrieve the arguments to use when generating the final string representation using the format string. * - * @return array + * @return array */ public function getArgs() { diff --git a/src/Contao/View/Contao2BackendView/Event/ParentViewChildRecordEvent.php b/src/Contao/View/Contao2BackendView/Event/ParentViewChildRecordEvent.php index 8fb46f4a..6e746e7d 100644 --- a/src/Contao/View/Contao2BackendView/Event/ParentViewChildRecordEvent.php +++ b/src/Contao/View/Contao2BackendView/Event/ParentViewChildRecordEvent.php @@ -37,7 +37,7 @@ class ParentViewChildRecordEvent extends AbstractModelAwareEvent * * @var string */ - protected $html; + protected $html = ''; /** * Set the html code to use as child record. diff --git a/src/Contao/View/Contao2BackendView/Event/PrepareMultipleModelsActionEvent.php b/src/Contao/View/Contao2BackendView/Event/PrepareMultipleModelsActionEvent.php index f55148f3..9fe5e74c 100644 --- a/src/Contao/View/Contao2BackendView/Event/PrepareMultipleModelsActionEvent.php +++ b/src/Contao/View/Contao2BackendView/Event/PrepareMultipleModelsActionEvent.php @@ -39,24 +39,24 @@ class PrepareMultipleModelsActionEvent extends AbstractActionAwareEvent /** * The model ids. * - * @var ModelIdInterface[] + * @var list */ - private $modelIds; + private array $modelIds; /** * The submit action. * * @var string */ - private $submitAction; + private string $submitAction; /** * Create a new instance. * - * @param EnvironmentInterface $environment The environment. - * @param Action $action The called action. - * @param ModelIdInterface[] $modelIds The list of model ids being parsed. - * @param string $submitAction The submit action name. + * @param EnvironmentInterface $environment The environment. + * @param Action $action The called action. + * @param list $modelIds The list of model ids being parsed. + * @param string $submitAction The submit action name. */ public function __construct(EnvironmentInterface $environment, Action $action, array $modelIds, $submitAction) { @@ -69,7 +69,7 @@ public function __construct(EnvironmentInterface $environment, Action $action, a /** * Get modelIds. * - * @return ModelIdInterface[] + * @return list */ public function getModelIds() { @@ -79,7 +79,7 @@ public function getModelIds() /** * Set the model ids. * - * @param ModelIdInterface[] $modelIds The new model ids. + * @param list $modelIds The new model ids. * * @return $this */ diff --git a/src/Contao/View/Contao2BackendView/EventListener/BackButtonListener.php b/src/Contao/View/Contao2BackendView/EventListener/BackButtonListener.php index 81c1fe37..1faba1d2 100644 --- a/src/Contao/View/Contao2BackendView/EventListener/BackButtonListener.php +++ b/src/Contao/View/Contao2BackendView/EventListener/BackButtonListener.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2021 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,8 @@ * @package contao-community-alliance/dc-general * @author Christian Schiffler * @author Sven Baumann - * @copyright 2013-2021 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -24,7 +25,10 @@ use ContaoCommunityAlliance\Contao\Bindings\Events\System\GetReferrerEvent; use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminatorAwareTrait; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\GetGlobalButtonEvent; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; +use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; /** * This handles the back button event in list views. @@ -52,6 +56,7 @@ public function handle(GetGlobalButtonEvent $event) $environment = $event->getEnvironment(); $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); if ( !( @@ -82,15 +87,21 @@ public function handle(GetGlobalButtonEvent $event) */ private function getReferrerUrl(EnvironmentInterface $environment) { + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + $parent = $environment->getParentDataDefinition(); $event = new GetReferrerEvent( true, (null !== $parent) ? $parent->getName() - : $environment->getDataDefinition()->getName() + : $definition->getName() ); - $environment->getEventDispatcher()->dispatch($event, ContaoEvents::SYSTEM_GET_REFERRER); + $dispatcher->dispatch($event, ContaoEvents::SYSTEM_GET_REFERRER); return $event->getReferrerUrl(); } diff --git a/src/Contao/View/Contao2BackendView/EventListener/ColorPickerWizardListener.php b/src/Contao/View/Contao2BackendView/EventListener/ColorPickerWizardListener.php index 7237f44a..d075341a 100644 --- a/src/Contao/View/Contao2BackendView/EventListener/ColorPickerWizardListener.php +++ b/src/Contao/View/Contao2BackendView/EventListener/ColorPickerWizardListener.php @@ -29,6 +29,8 @@ use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\BuildWidgetEvent; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\Properties\PropertyInterface; use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; +use ContaoCommunityAlliance\Translator\TranslatorInterface; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; /** * Widget Builder to append color picker wizards to Contao backend widgets. @@ -89,8 +91,11 @@ public static function getWizard($propInfo, EnvironmentInterface $environment) $wizard = ''; $propExtra = $propInfo->getExtra(); - if (\is_array($propExtra) && \array_key_exists('colorpicker', $propExtra) && $propExtra['colorpicker']) { - $pickerText = $environment->getTranslator()->translate('MSC.colorpicker'); + $translator = $environment->getTranslator(); + assert($translator instanceof TranslatorInterface); + + if (\array_key_exists('colorpicker', $propExtra) && $propExtra['colorpicker']) { + $pickerText = $translator->translate('MSC.colorpicker'); $event = new GenerateHtmlEvent( 'pickcolor.svg', $pickerText, @@ -102,7 +107,10 @@ public static function getWizard($propInfo, EnvironmentInterface $environment) ) ); - $environment->getEventDispatcher()->dispatch($event, ContaoEvents::IMAGE_GET_HTML); + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $dispatcher->dispatch($event, ContaoEvents::IMAGE_GET_HTML); // Support single fields as well (see contao/core#5240) $strKey = $propExtra['multiple'] ? $propInfo->getName() . '_0' : $propInfo->getName(); @@ -112,7 +120,7 @@ public static function getWizard($propInfo, EnvironmentInterface $environment) 'id: "ctrl_%3$s", startColor: ((cl = $("ctrl_%3$s").value.hexToRgb(true)) ? cl : [255, 0, 0]),' . 'imgPath: "%4$s", onComplete: function(color) {$("ctrl_%3$s").value = color.hex.replace("#", "");}});' . '});', - $event->getHtml(), + $event->getHtml() ?? '', $propInfo->getName(), $strKey, 'assets/colorpicker/images/' diff --git a/src/Contao/View/Contao2BackendView/EventListener/CreateModelButtonListener.php b/src/Contao/View/Contao2BackendView/EventListener/CreateModelButtonListener.php index bba77a07..fd5d40e0 100644 --- a/src/Contao/View/Contao2BackendView/EventListener/CreateModelButtonListener.php +++ b/src/Contao/View/Contao2BackendView/EventListener/CreateModelButtonListener.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,18 +13,21 @@ * @package contao-community-alliance/dc-general * @author Christian Schiffler * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ namespace ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\EventListener; +use ContaoCommunityAlliance\DcGeneral\Clipboard\ClipboardInterface; use ContaoCommunityAlliance\DcGeneral\Clipboard\Filter; use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminatorAwareTrait; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\GetGlobalButtonEvent; use ContaoCommunityAlliance\DcGeneral\Data\ModelId; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\BasicDefinitionInterface; +use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; use ContaoCommunityAlliance\UrlBuilder\UrlBuilder; /** @@ -52,11 +55,18 @@ public function handle(GetGlobalButtonEvent $event) } $environment = $event->getEnvironment(); - $basicDefinition = $environment->getDataDefinition()->getBasicDefinition(); - $mode = $basicDefinition->getMode(); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $basicDefinition = $definition->getBasicDefinition(); + assert($basicDefinition instanceof BasicDefinitionInterface); + + $mode = $basicDefinition->getMode(); if (!$basicDefinition->isCreatable()) { $event->setHtml(''); + return; } @@ -65,21 +75,33 @@ public function handle(GetGlobalButtonEvent $event) || (BasicDefinitionInterface::MODE_HIERARCHICAL === $mode) ) { $filter = new Filter(); - $filter->andModelIsFromProvider($basicDefinition->getDataProvider()); + + $provider = $basicDefinition->getDataProvider(); + assert(\is_string($provider)); + + $filter->andModelIsFromProvider($provider); if ($parentProviderName = $basicDefinition->getParentDataProvider()) { $filter->andParentIsFromProvider($parentProviderName); } else { $filter->andHasNoParent(); } - if ($environment->getClipboard()->isNotEmpty($filter)) { + $clipboard = $environment->getClipboard(); + assert($clipboard instanceof ClipboardInterface); + + if ($clipboard->isNotEmpty($filter)) { $event->setHtml(''); + return; } } $url = UrlBuilder::fromUrl($event->getHref()); - if ($serializedPid = $environment->getInputProvider()->getParameter('pid')) { + + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + + if ($serializedPid = $inputProvider->getParameter('pid')) { $url->setQueryParameter('pid', ModelId::fromSerialized($serializedPid)->getSerialized()); } diff --git a/src/Contao/View/Contao2BackendView/EventListener/CreateSubHeadlineListener.php b/src/Contao/View/Contao2BackendView/EventListener/CreateSubHeadlineListener.php index 955ebabb..6665c7d1 100644 --- a/src/Contao/View/Contao2BackendView/EventListener/CreateSubHeadlineListener.php +++ b/src/Contao/View/Contao2BackendView/EventListener/CreateSubHeadlineListener.php @@ -20,6 +20,8 @@ namespace ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\EventListener; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\GetEditMaskSubHeadlineEvent; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; +use ContaoCommunityAlliance\Translator\TranslatorInterface; /** * This class handles events to handle sub-headline at input mask. @@ -53,9 +55,15 @@ public function __invoke(GetEditMaskSubHeadlineEvent $event): void */ private function createSubHeadline(string $status, GetEditMaskSubHeadlineEvent $event): void { - $environment = $event->getEnvironment(); - $definitionName = $environment->getDataDefinition()->getName(); - $translator = $environment->getTranslator(); + $environment = $event->getEnvironment(); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $definitionName = $definition->getName(); + + $translator = $environment->getTranslator(); + assert($translator instanceof TranslatorInterface); $headline = $translator->translate($status, $definitionName, [$event->getModel()->getId()]); diff --git a/src/Contao/View/Contao2BackendView/EventListener/SelectModeButtonsListener.php b/src/Contao/View/Contao2BackendView/EventListener/SelectModeButtonsListener.php index 96031e3d..f143c04c 100644 --- a/src/Contao/View/Contao2BackendView/EventListener/SelectModeButtonsListener.php +++ b/src/Contao/View/Contao2BackendView/EventListener/SelectModeButtonsListener.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -12,7 +12,7 @@ * * @package contao-community-alliance/dc-general * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -22,6 +22,8 @@ use Contao\StringUtil; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\GetSelectModeButtonsEvent; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\ViewHelpers; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; +use ContaoCommunityAlliance\Translator\TranslatorInterface; /** * This class handle for add the default buttons for the select mode. @@ -37,9 +39,15 @@ class SelectModeButtonsListener */ public function handleEvent(GetSelectModeButtonsEvent $event) { - $environment = $event->getEnvironment(); - $translator = $environment->getTranslator(); - $basicDefinition = $environment->getDataDefinition()->getBasicDefinition(); + $environment = $event->getEnvironment(); + + $translator = $environment->getTranslator(); + assert($translator instanceof TranslatorInterface); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $basicDefinition = $definition->getBasicDefinition(); $buttons = []; $confirmMessage = \htmlentities( diff --git a/src/Contao/View/Contao2BackendView/FileSelect.php b/src/Contao/View/Contao2BackendView/FileSelect.php index 72d677b3..b3bd27c9 100644 --- a/src/Contao/View/Contao2BackendView/FileSelect.php +++ b/src/Contao/View/Contao2BackendView/FileSelect.php @@ -37,12 +37,19 @@ use ContaoCommunityAlliance\DcGeneral\Contao\Callback\Callbacks; use ContaoCommunityAlliance\DcGeneral\Contao\Compatibility\DcCompat; use ContaoCommunityAlliance\DcGeneral\Contao\InputProvider; +use ContaoCommunityAlliance\DcGeneral\Data\DataProviderInterface; use ContaoCommunityAlliance\DcGeneral\Data\ModelId; use ContaoCommunityAlliance\DcGeneral\Data\ModelIdInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\DcGeneral; +use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; use ContaoCommunityAlliance\DcGeneral\Factory\DcGeneralFactory; +use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; +use ContaoCommunityAlliance\DcGeneral\SessionStorageInterface; use ContaoCommunityAlliance\Translator\Contao\LangArrayTranslator; use ContaoCommunityAlliance\Translator\TranslatorChain; +use ContaoCommunityAlliance\Translator\TranslatorInterface; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; /** * Class FileSelect. @@ -55,19 +62,12 @@ */ class FileSelect { - /** - * Current ajax object. - * - * @var object - */ - protected $objAjax; - /** * The DcGeneral Object. * - * @var DcGeneral + * @var DcGeneral|null */ - protected $itemContainer; + protected $itemContainer = null; /** * Initialize the controller. @@ -84,9 +84,11 @@ public function __construct() BackendUser::getInstance(); Config::getInstance(); Database::getInstance(); + /** @psalm-suppress DeprecatedMethod */ BackendUser::getInstance()->authenticate(); System::loadLanguageFile('default'); + /** @psalm-suppress DeprecatedMethod */ Backend::setStaticUrls(); } @@ -102,7 +104,8 @@ public function run() { $inputProvider = new InputProvider(); - $template = new BackendTemplate('be_picker'); + $template = new BackendTemplate('be_picker'); + /** @psalm-suppress UndefinedMagicPropertyAssignment */ $template->main = ''; $ajax = $this->runAjaxRequest(); @@ -111,7 +114,14 @@ public function run() $this->setupItemContainer($modelId); - $sessionStorage = $this->itemContainer->getEnvironment()->getSessionStorage(); + $itemContainer = $this->itemContainer; + assert($itemContainer instanceof DcGeneral); + + $environment = $itemContainer->getEnvironment(); + assert($environment instanceof EnvironmentInterface); + + $sessionStorage = $environment->getSessionStorage(); + assert($sessionStorage instanceof SessionStorageInterface); // Define the current ID. \define( @@ -122,17 +132,31 @@ public function run() $fileSelector = $this->prepareFileSelector($modelId, $ajax); - $template->main = $fileSelector->generate(); - $template->theme = Backend::getTheme(); - $template->base = Environment::get('base'); - $template->language = $GLOBALS['TL_LANGUAGE']; - $template->title = StringUtil::specialchars($GLOBALS['TL_LANG']['MSC']['treepicker']); - $template->charset = $GLOBALS['TL_CONFIG']['characterSet']; - $template->addSearch = $fileSelector->searchField; - $template->search = $GLOBALS['TL_LANG']['MSC']['search']; - $template->action = StringUtil::ampersand(Environment::get('request')); - $template->value = $sessionStorage->get('file_selector_search'); - $template->manager = $GLOBALS['TL_LANG']['MSC']['treepickerManager']; + /** @psalm-suppress UndefinedMagicPropertyAssignment */ + $template->main = $fileSelector->generate(); + /** @psalm-suppress UndefinedMagicPropertyAssignment */ + $template->theme = Backend::getTheme(); + /** @psalm-suppress UndefinedMagicPropertyAssignment */ + $template->base = Environment::get('base'); + /** @psalm-suppress UndefinedMagicPropertyAssignment */ + $template->language = $GLOBALS['TL_LANGUAGE']; + /** @psalm-suppress UndefinedMagicPropertyAssignment */ + $template->title = StringUtil::specialchars($GLOBALS['TL_LANG']['MSC']['treepicker']); + /** @psalm-suppress UndefinedMagicPropertyAssignment */ + $template->charset = $GLOBALS['TL_CONFIG']['characterSet']; + /** + * @psalm-suppress UndefinedMagicPropertyAssignment + * @psalm-suppress UndefinedMagicPropertyFetch + */ + $template->addSearch = $fileSelector->searchField; + /** @psalm-suppress UndefinedMagicPropertyAssignment */ + $template->search = $GLOBALS['TL_LANG']['MSC']['search']; + $template->action = StringUtil::ampersand(Environment::get('request')); + /** @psalm-suppress UndefinedMagicPropertyAssignment */ + $template->value = $sessionStorage->get('file_selector_search'); + /** @psalm-suppress UndefinedMagicPropertyAssignment */ + $template->manager = $GLOBALS['TL_LANG']['MSC']['treepickerManager']; + /** @psalm-suppress UndefinedMagicPropertyAssignment */ $template->managerHref = ''; if ( @@ -141,23 +165,31 @@ public function run() ) { Backend::addFilesBreadcrumb('tl_files_picker'); } + /** @psalm-suppress UndefinedMagicPropertyAssignment */ $template->breadcrumb = $GLOBALS['TL_DCA']['tl_files']['list']['sorting']['breadcrumb']; $user = BackendUser::getInstance(); // Add the manager link. + /** @psalm-suppress UndefinedMethod */ if ($user->hasAccess('files', 'modules')) { - $template->manager = $GLOBALS['TL_LANG']['MSC']['fileManager']; + /** @psalm-suppress UndefinedMagicPropertyAssignment */ + $template->manager = $GLOBALS['TL_LANG']['MSC']['fileManager']; + /** @psalm-suppress UndefinedMagicPropertyAssignment */ $template->managerHref = 'contao/main.php?do=files&popup=1'; } + /** @psalm-suppress UndefinedMethod */ if (Input::get('switch') && $user->hasAccess('page', 'modules')) { - $template->switch = $GLOBALS['TL_LANG']['MSC']['pagePicker']; + /** @psalm-suppress UndefinedMagicPropertyAssignment */ + $template->switch = $GLOBALS['TL_LANG']['MSC']['pagePicker']; + /** @psalm-suppress UndefinedMagicPropertyAssignment */ $template->switchHref = \str_replace('contao/file.php', 'contao/page.php', StringUtil::ampersand(Environment::get('request'))); } // Prevent debug output at all cost. $GLOBALS['TL_CONFIG']['debugMode'] = false; + /** @psalm-suppress DeprecatedMethod */ $template->output(); } @@ -173,7 +205,15 @@ private function getActiveModel(ModelIdInterface $modelId) if (!Database::getInstance()->tableExists($modelId->getDataProviderName())) { return null; } - $dataProvider = $this->itemContainer->getEnvironment()->getDataProvider(); + + $itemContainer = $this->itemContainer; + assert($itemContainer instanceof DcGeneral); + + $environment = $itemContainer->getEnvironment(); + assert($environment instanceof EnvironmentInterface); + + $dataProvider = $environment->getDataProvider(); + assert($dataProvider instanceof DataProviderInterface); return $dataProvider->fetch($dataProvider->getEmptyConfig()->setId($modelId->getId())); } @@ -184,13 +224,22 @@ private function getActiveModel(ModelIdInterface $modelId) * @param ModelIdInterface $modelId The model identifier. * @param Ajax $ajax The ajax request. * + * @psalm-suppress DeprecatedClass * @return FileSelector * * @SuppressWarnings(PHPMD.Superglobals) */ private function prepareFileSelector(ModelIdInterface $modelId, Ajax $ajax = null) { - $inputProvider = $this->itemContainer->getEnvironment()->getInputProvider(); + $itemContainer = $this->itemContainer; + assert($itemContainer instanceof DcGeneral); + + $environment = $itemContainer->getEnvironment(); + assert($environment instanceof EnvironmentInterface); + + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + $propertyName = $inputProvider->getParameter('field'); $information = (array) $GLOBALS['TL_DCA'][$modelId->getDataProviderName()]['fields'][$propertyName]; @@ -198,22 +247,29 @@ private function prepareFileSelector(ModelIdInterface $modelId, Ajax $ajax = nul $information['eval'] = []; } + $itemContainer = $this->itemContainer; + assert($itemContainer instanceof DcGeneral); + + $definition = $itemContainer->getEnvironment(); + assert($definition instanceof ContainerInterface); + // Merge with the information from the data container. - $property = $this - ->itemContainer - ->getEnvironment() - ->getDataDefinition() - ->getPropertiesDefinition() - ->getProperty($propertyName); + $property = $definition->getPropertiesDefinition()->getProperty($propertyName); $extra = $property->getExtra(); $information['eval'] = \array_merge($extra, (array) $information['eval']); - $this->session->set('filePickerRef', Environment::get('request')); + $sessionStorage = $environment->getSessionStorage(); + assert($sessionStorage instanceof SessionStorageInterface); + $sessionStorage->set('filePickerRef', Environment::get('request')); - $combat = new DcCompat($this->itemContainer->getEnvironment(), $this->getActiveModel($modelId), $propertyName); + $combat = new DcCompat($itemContainer->getEnvironment(), $this->getActiveModel($modelId), $propertyName); - /** @var FileSelector $fileSelector */ + /** + * @var FileSelector $fileSelector + * + * @psalm-suppress DeprecatedClass + */ $fileSelector = new $GLOBALS['BE_FFL']['fileSelector']( Widget::getAttributesFromDca( $information, @@ -247,7 +303,14 @@ private function prepareFileSelector(ModelIdInterface $modelId, Ajax $ajax = nul */ private function prepareValuesForFileSelector($propertyName, ModelIdInterface $modelId, DcCompat $combat) { - $inputProvider = $this->itemContainer->getEnvironment()->getInputProvider(); + $itemContainer = $this->itemContainer; + assert($itemContainer instanceof DcGeneral); + + $environment = $itemContainer->getEnvironment(); + assert($environment instanceof EnvironmentInterface); + + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); $fileSelectorValues = []; foreach (\array_filter(\explode(',', $inputProvider->getParameter('value'))) as $k => $v) { @@ -273,7 +336,7 @@ private function prepareValuesForFileSelector($propertyName, ModelIdInterface $m } /** - * Setup the item container. + * Set up the item container. * * @param ModelIdInterface $modelId The model identifier. * @@ -284,7 +347,11 @@ private function prepareValuesForFileSelector($propertyName, ModelIdInterface $m private function setupItemContainer(ModelIdInterface $modelId) { $dispatcher = System::getContainer()->get('event_dispatcher'); + assert($dispatcher instanceof EventDispatcherInterface); + $translator = new TranslatorChain(); + assert($translator instanceof TranslatorInterface); + $translator->add(new LangArrayTranslator($dispatcher)); $this->itemContainer = (new DcGeneralFactory()) diff --git a/src/Contao/View/Contao2BackendView/Filter/LanguageFilter.php b/src/Contao/View/Contao2BackendView/Filter/LanguageFilter.php index 7e05d27c..5554fba2 100644 --- a/src/Contao/View/Contao2BackendView/Filter/LanguageFilter.php +++ b/src/Contao/View/Contao2BackendView/Filter/LanguageFilter.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2021 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -16,7 +16,7 @@ * @author David Molineus * @author Sven Baumann * @author Ingolf Steinhardt - * @copyright 2013-2021 Contao Community Alliance. + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -26,16 +26,24 @@ use ContaoCommunityAlliance\Contao\Bindings\ContaoEvents; use ContaoCommunityAlliance\Contao\Bindings\Events\Controller\ReloadEvent; use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminator; +use ContaoCommunityAlliance\DcGeneral\Controller\ControllerInterface; +use ContaoCommunityAlliance\DcGeneral\Data\LanguageInformationInterface; use ContaoCommunityAlliance\DcGeneral\Data\ModelId; +use ContaoCommunityAlliance\DcGeneral\Data\ModelIdInterface; use ContaoCommunityAlliance\DcGeneral\Data\MultiLanguageDataProviderInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\DcGeneralEvents; use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; use ContaoCommunityAlliance\DcGeneral\Event\ActionEvent; use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; +use ContaoCommunityAlliance\DcGeneral\SessionStorageInterface; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\EventDispatcher\EventSubscriberInterface; /** * Handler class for handling the show events. + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class LanguageFilter implements EventSubscriberInterface { @@ -83,7 +91,7 @@ public function handleAction(ActionEvent $event) } /** - * Check if the data provider is multi language and prepare the data provider with the selected language. + * Check if the data provider is multi-language and prepare the data provider with the selected language. * * @param EnvironmentInterface $environment The environment. * @param bool $resetToFallback Flag if the language must be reset to the fallback. @@ -96,26 +104,45 @@ public function handleAction(ActionEvent $event) private function checkLanguage($environment, $resetToFallback) { $sessionStorage = $environment->getSessionStorage(); - $dataProvider = $environment->getDataProvider(); - $providerName = $environment->getDataDefinition()->getName(); - $modelId = $this->modelIdFromInput($environment->getInputProvider()); - $languages = $environment->getController()->getSupportedLanguages($modelId); + assert($sessionStorage instanceof SessionStorageInterface); + + $dataProvider = $environment->getDataProvider(); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $providerName = $definition->getName(); + + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + + $modelId = $this->modelIdFromInput($inputProvider); + + $controller = $environment->getController(); + assert($controller instanceof ControllerInterface); + + $languages = $controller->getSupportedLanguages($modelId); if (!$languages) { return; } - // Exit out when not a multi language provider. + // Exit out when not a multi-language provider. if (!($dataProvider instanceof MultiLanguageDataProviderInterface)) { return; } // If a new item, we MUST reset to the fallback as that is the first language that has to be stored // and set this language to session. + $session = []; if ((null === $modelId) && $resetToFallback) { - $dataProvider->setCurrentLanguage($dataProvider->getFallbackLanguage(null)->getLocale()); + $fallbackLanguage = $dataProvider->getFallbackLanguage(null); + assert($fallbackLanguage instanceof LanguageInformationInterface); + + $dataProvider->setCurrentLanguage($fallbackLanguage->getLocale()); $session['ml_support'][$providerName] = $dataProvider->getCurrentLanguage(); $sessionStorage->set('dc_general', $session); + return; } @@ -128,7 +155,10 @@ private function checkLanguage($environment, $resetToFallback) $currentLanguage = ($session['ml_support'][$providerName] ?? $GLOBALS['TL_LANGUAGE']); if (!\array_key_exists($currentLanguage, $languages)) { - $currentLanguage = $dataProvider->getFallbackLanguage($modelId)->getLocale(); + $fallbackLanguage = $dataProvider->getFallbackLanguage($modelId); + assert($fallbackLanguage instanceof LanguageInformationInterface); + + $currentLanguage = $fallbackLanguage->getLocale(); } $session['ml_support'][$providerName] = $currentLanguage; @@ -150,21 +180,33 @@ private function checkLanguage($environment, $resetToFallback) private function checkLanguageSubmit($environment, $languages) { $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); if ('language_switch' !== $inputProvider->getValue('FORM_SUBMIT')) { return; } // Get/Check the new language. + $session = []; if ( $inputProvider->hasValue('language') && \array_key_exists($inputProvider->getValue('language'), $languages) ) { - $session['ml_support'][$environment->getDataDefinition()->getName()] = $inputProvider->getValue('language'); - $environment->getSessionStorage()->set('dc_general', $session); + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $session['ml_support'][$definition->getName()] = $inputProvider->getValue('language'); + + $sessionStorage = $environment->getSessionStorage(); + assert($sessionStorage instanceof SessionStorageInterface); + + $sessionStorage->set('dc_general', $session); } - $environment->getEventDispatcher()->dispatch(new ReloadEvent(), ContaoEvents::CONTROLLER_RELOAD); + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $dispatcher->dispatch(new ReloadEvent(), ContaoEvents::CONTROLLER_RELOAD); } /** @@ -172,7 +214,7 @@ private function checkLanguageSubmit($environment, $languages) * * @param InputProviderInterface $inputProvider The input provider. * - * @return mixed|null + * @return ModelIdInterface|null */ private function modelIdFromInput(InputProviderInterface $inputProvider) { diff --git a/src/Contao/View/Contao2BackendView/GlobalButtonRenderer.php b/src/Contao/View/Contao2BackendView/GlobalButtonRenderer.php index 11f6e40d..129169d6 100644 --- a/src/Contao/View/Contao2BackendView/GlobalButtonRenderer.php +++ b/src/Contao/View/Contao2BackendView/GlobalButtonRenderer.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2022 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -15,7 +15,7 @@ * @author Ingolf Steinhardt * @author Sven Baumann * @author David Molineus - * @copyright 2013-2022 Contao Community Alliance. + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -28,6 +28,7 @@ use ContaoCommunityAlliance\DcGeneral\Contao\DataDefinition\Definition\Contao2BackendViewDefinitionInterface; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\GetGlobalButtonEvent; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\GetGlobalButtonsEvent; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\CommandInterface; use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; use ContaoCommunityAlliance\Translator\TranslatorInterface; @@ -67,8 +68,15 @@ class GlobalButtonRenderer public function __construct(EnvironmentInterface $environment) { $this->environment = $environment; - $this->dispatcher = $environment->getEventDispatcher(); - $this->translator = $environment->getTranslator(); + assert($environment instanceof EnvironmentInterface); + + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + $this->dispatcher = $dispatcher; + + $translator = $environment->getTranslator(); + assert($translator instanceof TranslatorInterface); + $this->translator = $translator; } /** @@ -78,17 +86,13 @@ public function __construct(EnvironmentInterface $environment) */ public function render() { - /** @var CommandInterface[] $commands */ - $commands = $this - ->environment - ->getDataDefinition() - ->getDefinition(Contao2BackendViewDefinitionInterface::NAME) - ->getGlobalCommands() - ->getCommands(); - - if (!\is_array($commands)) { - $commands = []; - } + $definition = $this->environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $backendView = $definition->getDefinition(Contao2BackendViewDefinitionInterface::NAME); + assert($backendView instanceof Contao2BackendViewDefinitionInterface); + + $commands = $backendView->getGlobalCommands()->getCommands(); $buttons = []; foreach ($commands as $command) { @@ -134,19 +138,19 @@ private function renderButton(CommandInterface $command) $href = $event->getUrl(); } - if (null === $label) { + if ('' === $label) { $label = $command->getName(); } $buttonEvent = new GetGlobalButtonEvent($this->environment); $buttonEvent - ->setAccessKey(isset($extra['accesskey']) ? \trim($extra['accesskey']) : null) + ->setAccessKey(isset($extra['accesskey']) ? \trim($extra['accesskey']) : '') ->setAttributes(' ' . \ltrim($extra['attributes'] ?? '')) ->setClass($extra['class']) ->setKey($command->getName()) ->setHref($href) ->setLabel($label) - ->setTitle($this->translate((string) $command->getDescription())); + ->setTitle($this->translate($command->getDescription())); $this->dispatcher->dispatch($buttonEvent, GetGlobalButtonEvent::NAME); // Allow to override the button entirely - if someone sets empty string, we keep it. @@ -174,7 +178,10 @@ private function renderButton(CommandInterface $command) */ private function translate($path) { - $value = $this->translator->translate($path, $this->environment->getDataDefinition()->getName()); + $definition = $this->environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $value = $this->translator->translate($path, $definition->getName()); if ($path !== $value) { return $value; } diff --git a/src/Contao/View/Contao2BackendView/PanelBuilder.php b/src/Contao/View/Contao2BackendView/PanelBuilder.php index b391f4e1..60e45f31 100644 --- a/src/Contao/View/Contao2BackendView/PanelBuilder.php +++ b/src/Contao/View/Contao2BackendView/PanelBuilder.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,8 @@ * @package contao-community-alliance/dc-general * @author Christian Schiffler * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -21,6 +22,7 @@ namespace ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView; use ContaoCommunityAlliance\DcGeneral\Contao\DataDefinition\Definition\Contao2BackendViewDefinitionInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\Panel\ElementInformationInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\Panel\FilterElementInformationInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\Panel\LimitElementInformationInterface; @@ -47,9 +49,9 @@ class PanelBuilder /** * The environment. * - * @var Contao2BackendViewDefinitionInterface + * @var EnvironmentInterface */ - private $environment; + private EnvironmentInterface $environment; /** * Create a new instance. @@ -71,15 +73,15 @@ public function build() $panel = new DefaultPanelContainer(); $panel->setEnvironment($this->environment); - /** @var Contao2BackendViewDefinitionInterface $viewDefinition */ - $viewDefinition = $this - ->environment - ->getDataDefinition() - ->getDefinition(Contao2BackendViewDefinitionInterface::NAME); + $definition = $this->environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $viewDefinition = $definition->getDefinition(Contao2BackendViewDefinitionInterface::NAME); + assert($viewDefinition instanceof Contao2BackendViewDefinitionInterface); foreach ($viewDefinition->getPanelLayout()->getRows() as $panelKey => $row) { $panelRow = new DefaultPanel(); - $panel->addPanel($panelKey, $panelRow); + $panel->addPanel((string) $panelKey, $panelRow); foreach ($row as $element) { /** @var ElementInformationInterface $element */ if (null !== $instance = $this->createElement($element)) { diff --git a/src/Contao/View/Contao2BackendView/PanelRenderer.php b/src/Contao/View/Contao2BackendView/PanelRenderer.php index 31ecae19..21c8246a 100644 --- a/src/Contao/View/Contao2BackendView/PanelRenderer.php +++ b/src/Contao/View/Contao2BackendView/PanelRenderer.php @@ -29,8 +29,11 @@ use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\GetPanelElementTemplateEvent; use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralRuntimeException; +use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; +use ContaoCommunityAlliance\DcGeneral\Panel\PanelContainerInterface; use ContaoCommunityAlliance\DcGeneral\Panel\PanelElementInterface; use ContaoCommunityAlliance\DcGeneral\Panel\PanelInterface; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; /** * This class renders a backend view panel including all elements. @@ -57,7 +60,7 @@ public function __construct($view) /** * Retrieve the environment. * - * @return EnvironmentInterface + * @return EnvironmentInterface|null */ protected function getEnvironment() { @@ -75,9 +78,13 @@ protected function getEnvironment() protected function renderPanelElement($element, $cssClass) { $environment = $this->getEnvironment(); + assert($environment instanceof EnvironmentInterface); + + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); $event = new GetPanelElementTemplateEvent($environment, $element); - $environment->getEventDispatcher()->dispatch($event, $event::NAME); + $dispatcher->dispatch($event, $event::NAME); $template = $event->getTemplate(); @@ -105,7 +112,7 @@ protected function isIgnoredPanel(PanelElementInterface $element, $ignoredPanels return false; } - foreach ((array) $ignoredPanels as $class) { + foreach ($ignoredPanels as $class) { if ($element instanceof $class) { return true; } @@ -173,9 +180,13 @@ protected function renderPanelRow($panel, $ignoredPanels) public function render($ignoredPanels = []) { $environment = $this->getEnvironment(); + assert($environment instanceof EnvironmentInterface); + + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); // If in edit/override all mode and list all properties, the panel filter isn´t in use. - if ('properties' === $environment->getInputProvider()->getParameter('select')) { + if ('properties' === $inputProvider->getParameter('select')) { return ''; } @@ -183,8 +194,11 @@ public function render($ignoredPanels = []) throw new DcGeneralRuntimeException('No panel information stored in data container.'); } + $panelContainer = $this->view->getPanel(); + assert($panelContainer instanceof PanelContainerInterface); + $panels = []; - foreach ($this->view->getPanel() as $panel) { + foreach ($panelContainer as $panel) { $panels[] = $this->renderPanelRow($panel, $ignoredPanels); } @@ -192,10 +206,13 @@ public function render($ignoredPanels = []) $template = new ContaoBackendViewTemplate('dcbe_general_panel'); $themeEvent = new GetThemeEvent(); - $environment->getEventDispatcher()->dispatch($themeEvent, ContaoEvents::BACKEND_GET_THEME); + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $dispatcher->dispatch($themeEvent, ContaoEvents::BACKEND_GET_THEME); $template - ->set('action', StringUtil::ampersand($environment->getInputProvider()->getRequestUrl(), true)) + ->set('action', StringUtil::ampersand($inputProvider->getRequestUrl(), true)) ->set('theme', $themeEvent->getTheme()) ->set('panel', $panels); diff --git a/src/Contao/View/Contao2BackendView/ParentView.php b/src/Contao/View/Contao2BackendView/ParentView.php index ea164a21..d8e03384 100644 --- a/src/Contao/View/Contao2BackendView/ParentView.php +++ b/src/Contao/View/Contao2BackendView/ParentView.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -16,13 +16,15 @@ * @author Tristan Lins * @author David Molineus * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ namespace ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView; +use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; use ContaoCommunityAlliance\DcGeneral\Event\EnforceModelRelationshipEvent; use ContaoCommunityAlliance\DcGeneral\EventListener\ModelRelationship\ParentEnforcingListener; @@ -42,7 +44,10 @@ class ParentView extends BaseView */ public function enforceModelRelationship($model) { + $environment = $this->getEnvironment(); + assert($environment instanceof EnvironmentInterface); + // Fallback implementation. - (new ParentEnforcingListener())->process(new EnforceModelRelationshipEvent($this->getEnvironment(), $model)); + (new ParentEnforcingListener())->process(new EnforceModelRelationshipEvent($environment, $model)); } } diff --git a/src/Contao/View/Contao2BackendView/Subscriber/CheckPermission.php b/src/Contao/View/Contao2BackendView/Subscriber/CheckPermission.php index 57588b04..89011350 100644 --- a/src/Contao/View/Contao2BackendView/Subscriber/CheckPermission.php +++ b/src/Contao/View/Contao2BackendView/Subscriber/CheckPermission.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,8 @@ * @package contao-community-alliance/dc-general * @author Sven Baumann * @author Christian Schiffler - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -126,7 +127,9 @@ public function checkPermissionIsEditable(BuildDataDefinitionEvent $event) return; } - $view = $container->getDefinition(Contao2BackendViewDefinitionInterface::NAME); + $view = $container->getDefinition(Contao2BackendViewDefinitionInterface::NAME); + assert($view instanceof Contao2BackendViewDefinitionInterface); + $modelCommands = $view->getModelCommands(); $this->disableCommandByActionName($modelCommands, 'edit'); @@ -153,7 +156,9 @@ public function checkPermissionIsDeletable(BuildDataDefinitionEvent $event) return; } - $view = $container->getDefinition(Contao2BackendViewDefinitionInterface::NAME); + $view = $container->getDefinition(Contao2BackendViewDefinitionInterface::NAME); + assert($view instanceof Contao2BackendViewDefinitionInterface); + $modelCommands = $view->getModelCommands(); $this->disableCommandByActionName($modelCommands, 'delete'); @@ -178,7 +183,9 @@ public function checkPermissionIsCreatable(BuildDataDefinitionEvent $event) return; } - $view = $container->getDefinition(Contao2BackendViewDefinitionInterface::NAME); + $view = $container->getDefinition(Contao2BackendViewDefinitionInterface::NAME); + assert($view instanceof Contao2BackendViewDefinitionInterface); + $modelCommands = $view->getModelCommands(); $this->disableCommandByActionName($modelCommands, 'copy'); @@ -210,7 +217,7 @@ private function getVisibilityConditionChain($property) /** * Disable command by action name. * - * @param CommandCollectionInterface $commands The commands collection. + * @param CommandCollectionInterface $commands The command's collection. * @param string $actionName The action name. * * @return void diff --git a/src/Contao/View/Contao2BackendView/Subscriber/GetGroupHeaderSubscriber.php b/src/Contao/View/Contao2BackendView/Subscriber/GetGroupHeaderSubscriber.php index 3528fac1..711f68fb 100644 --- a/src/Contao/View/Contao2BackendView/Subscriber/GetGroupHeaderSubscriber.php +++ b/src/Contao/View/Contao2BackendView/Subscriber/GetGroupHeaderSubscriber.php @@ -31,6 +31,7 @@ use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\GetGroupHeaderEvent; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\ViewHelpers; use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\Properties\PropertyInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\GroupAndSortingInformationInterface; use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; @@ -86,17 +87,18 @@ public function handle(GetGroupHeaderEvent $event) } $environment = $event->getEnvironment(); - $property = $environment - ->getDataDefinition() - ->getPropertiesDefinition() - ->getProperty($event->getGroupField()); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); // No property? Get out! - if (!$property) { + if (!$definition->getPropertiesDefinition()->hasProperty($event->getGroupField())) { $event->setValue('-'); return; } + $property = $definition->getPropertiesDefinition()->getProperty($event->getGroupField()); + $value = $this->formatGroupHeader( $environment, $event->getModel(), @@ -116,10 +118,10 @@ public function handle(GetGroupHeaderEvent $event) * @param EnvironmentInterface $environment The environment. * @param ModelInterface $model The model. * @param PropertyInterface $property The property. - * @param int $groupingMode The grouping mode. + * @param string $groupingMode The grouping mode. * @param int $groupingLength The grouping length. * - * @return string + * @return string|null */ protected function formatGroupHeader( EnvironmentInterface $environment, @@ -140,10 +142,10 @@ protected function formatGroupHeader( $value = ViewHelpers::getReadableFieldValue($environment, $property, $model); if (isset($evaluation['reference'])) { - $remoteNew = $evaluation['reference'][$value]; + $remoteNew = $evaluation['reference'][$value] ?? null; } elseif (ArrayUtil::isAssoc($property->getOptions())) { $options = $property->getOptions(); - $remoteNew = $options[$value]; + $remoteNew = $options[$value] ?? null; } else { $remoteNew = $value; } @@ -176,13 +178,13 @@ private function formatCheckboxOptionLabel($value) /** * Format the group header by the grouping mode. * - * @param int $groupingMode The grouping mode. + * @param string $groupingMode The grouping mode. * @param int $groupingLength The grouping length. * @param EnvironmentInterface $environment The environment. * @param PropertyInterface $property The current property definition. * @param ModelInterface $model The current data model. * - * @return string + * @return string|null */ private function formatByGroupingMode( $groupingMode, @@ -226,7 +228,7 @@ private function formatByCharGrouping($value, $groupingLength) return '-'; } - return mb_strtoupper(mb_substr($value, 0, $groupingLength ?: null)); + return \mb_strtoupper(\mb_substr($value, 0, $groupingLength ?: null)); } /** @@ -234,14 +236,16 @@ private function formatByCharGrouping($value, $groupingLength) * * @param int $value The value. * - * @return string + * @return string|null */ - private function formatByDayGrouping($value) + private function formatByDayGrouping(int $value): ?string { $value = $this->getTimestamp($value); - if ('' === $value) { + + if (0 === $value) { return '-'; } + $event = new ParseDateEvent($value, Config::get('dateFormat')); $this->dispatcher->dispatch($event, ContaoEvents::DATE_PARSE); @@ -253,12 +257,13 @@ private function formatByDayGrouping($value) * * @param int $value The value. * - * @return string + * @return string|null */ - private function formatByMonthGrouping($value) + private function formatByMonthGrouping(int $value): ?string { $value = $this->getTimestamp($value); - if ('' === $value) { + + if (0 === $value) { return '-'; } $event = new ParseDateEvent($value, 'F Y'); @@ -277,11 +282,12 @@ private function formatByMonthGrouping($value) private function formatByYearGrouping($value) { $value = $this->getTimestamp($value); - if ('' === $value) { + + if (0 === $value) { return '-'; } - return date('Y', $value); + return \date('Y', $value); } /** diff --git a/src/Contao/View/Contao2BackendView/Subscriber/MultipleHandlerSubscriber.php b/src/Contao/View/Contao2BackendView/Subscriber/MultipleHandlerSubscriber.php index 8e0164a4..87beaade 100644 --- a/src/Contao/View/Contao2BackendView/Subscriber/MultipleHandlerSubscriber.php +++ b/src/Contao/View/Contao2BackendView/Subscriber/MultipleHandlerSubscriber.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2021 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -12,26 +12,35 @@ * * @package contao-community-alliance/dc-general * @author Sven Baumann - * @copyright 2013-2021 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0 * @filesource */ namespace ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Subscriber; +use Contao\Widget; use ContaoCommunityAlliance\DcGeneral\Contao\DataDefinition\Definition\Contao2BackendViewDefinitionInterface; use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminator; use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminatorAwareTrait; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\BuildWidgetEvent; use ContaoCommunityAlliance\DcGeneral\Data\ModelId; use ContaoCommunityAlliance\DcGeneral\Data\ModelIdInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\DcGeneralEvents; use ContaoCommunityAlliance\DcGeneral\Event\ActionEvent; +use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralException; +use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; +use ContaoCommunityAlliance\DcGeneral\SessionStorageInterface; use MenAtWork\MultiColumnWizardBundle\Event\GetOptionsEvent; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\EventDispatcher\EventSubscriberInterface; /** * The multiple handler subscriber provides functions for edit/override all. + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class MultipleHandlerSubscriber implements EventSubscriberInterface { @@ -63,7 +72,7 @@ public function __construct(RequestScopeDeterminator $scopeDeterminator) * * array('eventName' => array('methodName', $priority)) * * array('eventName' => array(array('methodName1', $priority), array('methodName2'))) * - * @return array The event names to listen to + * @return array> */ public static function getSubscribedEvents() { @@ -96,7 +105,11 @@ public function prepareGlobalAllButton(ActionEvent $event) } $dataDefinition = $event->getEnvironment()->getDataDefinition(); - $backendView = $dataDefinition->getDefinition(Contao2BackendViewDefinitionInterface::NAME); + assert($dataDefinition instanceof ContainerInterface); + + $backendView = $dataDefinition->getDefinition(Contao2BackendViewDefinitionInterface::NAME); + assert($backendView instanceof Contao2BackendViewDefinitionInterface); + $globalCommands = $backendView->getGlobalCommands(); if ($globalCommands->hasCommandNamed('all')) { @@ -108,7 +121,7 @@ public function prepareGlobalAllButton(ActionEvent $event) } /** - * Deactivate global button their are not useful. + * Deactivate global button there are not useful. * * @param ActionEvent $event The event. * @@ -125,7 +138,11 @@ public function deactivateGlobalButton(ActionEvent $event) } $dataDefinition = $event->getEnvironment()->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + $backendView = $dataDefinition->getDefinition(Contao2BackendViewDefinitionInterface::NAME); + assert($backendView instanceof Contao2BackendViewDefinitionInterface); + $globalCommands = $backendView->getGlobalCommands(); $allowedButton = ['close_all_button']; @@ -153,10 +170,13 @@ public function handleOriginalOptions(GetOptionsEvent $event) { $environment = $event->getEnvironment(); + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + if ( - !$this->getScopeDeterminator()->currentScopeIsBackend() - || ('select' !== $environment->getInputProvider()->getParameter('act')) - || ('edit' !== $environment->getInputProvider()->getParameter('select')) + ('select' !== $inputProvider->getParameter('act')) + || ('edit' !== $inputProvider->getParameter('select')) + || !$this->getScopeDeterminator()->currentScopeIsBackend() ) { return; } @@ -184,7 +204,10 @@ public function handleOriginalOptions(GetOptionsEvent $event) $event->getOptions() ); - $environment->getEventDispatcher()->dispatch($originalOptionsEvent, GetOptionsEvent::NAME); + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $dispatcher->dispatch($originalOptionsEvent, GetOptionsEvent::NAME); $event->setOptions($originalOptionsEvent->getOptions()); @@ -202,15 +225,21 @@ public function handleOriginalWidget(BuildWidgetEvent $event) { $environment = $event->getEnvironment(); + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + if ( - !$this->getScopeDeterminator()->currentScopeIsBackend() - || ('select' !== $environment->getInputProvider()->getParameter('act')) - || ('edit' !== $environment->getInputProvider()->getParameter('select')) + 'select' !== $inputProvider->getParameter('act') + || 'edit' !== $inputProvider->getParameter('select') + || !$this->getScopeDeterminator()->currentScopeIsBackend() ) { return; } - $properties = $environment->getDataDefinition()->getPropertiesDefinition(); + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $properties = $definition->getPropertiesDefinition(); $this->findModelIdByPropertyName($event); @@ -218,10 +247,8 @@ public function handleOriginalWidget(BuildWidgetEvent $event) $modelId = ModelId::fromModel($model); $originalPropertyName = $this->getOriginalPropertyName($event->getProperty()->getName(), $modelId); - if ( - (null === $originalPropertyName) - || ((null !== $originalPropertyName) && (false === $properties->hasProperty($originalPropertyName))) - ) { + + if (null === $originalPropertyName) { return; } @@ -244,15 +271,22 @@ public function handleOriginalWidget(BuildWidgetEvent $event) $originalEvent = new BuildWidgetEvent($environment, $model, $originalProperty); - $environment->getEventDispatcher()->dispatch($originalEvent, BuildWidgetEvent::NAME); + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); - $originalEvent->getWidget()->id = $event->getProperty()->getName(); - $originalEvent->getWidget()->name = + $dispatcher->dispatch($originalEvent, BuildWidgetEvent::NAME); + + $widget = $originalEvent->getWidget(); + assert($widget instanceof Widget); + + $widget->id = $event->getProperty()->getName(); + $widget->name = \str_replace('::', '____', $modelId->getSerialized()) . '_[' . $originalPropertyName . ']'; - $originalEvent->getWidget()->tl_class = ''; + /** @psalm-suppress UndefinedMagicPropertyAssignment */ + $widget->tl_class = ''; - $event->setWidget($originalEvent->getWidget()); + $event->setWidget($widget); $originalProperty->setExtra($originalExtra); @@ -272,24 +306,35 @@ private function findModelIdByPropertyName(BuildWidgetEvent $event) return; } - $environment = $event->getEnvironment(); + $environment = $event->getEnvironment(); + $dataDefinition = $environment->getDataDefinition(); - $inputProvider = $environment->getInputProvider(); + assert($dataDefinition instanceof ContainerInterface); + + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + $sessionStorage = $environment->getSessionStorage(); + assert($sessionStorage instanceof SessionStorageInterface); + /** @var array{models: list} $session */ $session = $sessionStorage->get($dataDefinition->getName() . '.' . $inputProvider->getParameter('mode')); $model = null; foreach ($session['models'] as $sessionModel) { $model = $sessionModel; - if (0 !== \strpos($event->getProperty()->getName(), \str_replace('::', '____', $sessionModel))) { + if (!str_starts_with($event->getProperty()->getName(), \str_replace('::', '____', $sessionModel))) { continue; } break; } + if (null === $model) { + throw new DcGeneralException('Failed to find a model.'); + } + $modelId = ModelId::fromSerialized($model); $event->getModel()->setId($modelId->getId()); diff --git a/src/Contao/View/Contao2BackendView/Subscriber/RichTextFileUuidSubscriber.php b/src/Contao/View/Contao2BackendView/Subscriber/RichTextFileUuidSubscriber.php index 63d5d33d..d3fe8400 100644 --- a/src/Contao/View/Contao2BackendView/Subscriber/RichTextFileUuidSubscriber.php +++ b/src/Contao/View/Contao2BackendView/Subscriber/RichTextFileUuidSubscriber.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,8 @@ * @package contao-community-alliance/dc-general * @author Sven Baumann * @author Christian Schiffler - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -24,6 +25,7 @@ use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminator; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\DecodePropertyValueForWidgetEvent; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\EncodePropertyValueFromWidgetEvent; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use Symfony\Component\EventDispatcher\EventSubscriberInterface; /** @@ -64,7 +66,7 @@ public function __construct(RequestScopeDeterminator $scopeDeterminator) * * array('eventName' => array('methodName', $priority)) * * array('eventName' => array(array('methodName1', $priority), array('methodName2'))) * - * @return array The event names to listen to + * @return array> */ public static function getSubscribedEvents() { @@ -93,7 +95,10 @@ public function convertFileSourceToUuid(EncodePropertyValueFromWidgetEvent $even return; } - $propertiesDefinition = $event->getEnvironment()->getDataDefinition()->getPropertiesDefinition(); + $definition = $event->getEnvironment()->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $propertiesDefinition = $definition->getPropertiesDefinition(); $property = $propertiesDefinition->getProperty($event->getProperty()); @@ -120,7 +125,10 @@ public function convertUuidToFileSource(DecodePropertyValueForWidgetEvent $event return; } - $propertiesDefinition = $event->getEnvironment()->getDataDefinition()->getPropertiesDefinition(); + $definition = $event->getEnvironment()->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $propertiesDefinition = $definition->getPropertiesDefinition(); $property = $propertiesDefinition->getProperty($event->getProperty()); diff --git a/src/Contao/View/Contao2BackendView/Subscriber/WidgetBuilder.php b/src/Contao/View/Contao2BackendView/Subscriber/WidgetBuilder.php index 16be0f3e..64f09b90 100644 --- a/src/Contao/View/Contao2BackendView/Subscriber/WidgetBuilder.php +++ b/src/Contao/View/Contao2BackendView/Subscriber/WidgetBuilder.php @@ -24,7 +24,6 @@ namespace ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Subscriber; -use Contao\CheckBox; use Contao\StringUtil; use Contao\Widget; use ContaoCommunityAlliance\Contao\Bindings\ContaoEvents; @@ -43,16 +42,21 @@ use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Widget\PageTreeOrder; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Widget\TreePickerOrder; use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\Properties\PropertyInterface; use ContaoCommunityAlliance\DcGeneral\EnvironmentAwareInterface; use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralRuntimeException; +use ContaoCommunityAlliance\Translator\TranslatorInterface; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; /** * Widget Builder build Contao backend widgets. * * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * + * @final */ class WidgetBuilder implements EnvironmentAwareInterface { @@ -61,19 +65,21 @@ class WidgetBuilder implements EnvironmentAwareInterface * * @var RequestScopeDeterminator */ - private static $scopeDeterminator; + private static RequestScopeDeterminator $scopeDeterminator; /** * The environment. * * @var EnvironmentInterface */ - private $environment; + private EnvironmentInterface $environment; /** * Mapping list of widget types where the DC General has it own widgets. * * @var array + * + * @psalm-suppress DeprecatedClass - we know. :D */ protected static $widgetMapping = [ 'fileTree' => FileTree::class, @@ -112,8 +118,10 @@ public static function handleEvent(BuildWidgetEvent $event) return; } - $event - ->setWidget((new static($event->getEnvironment()))->buildWidget($event->getProperty(), $event->getModel())); + $widget = (new static($event->getEnvironment()))->buildWidget($event->getProperty(), $event->getModel()); + assert($widget instanceof Widget); + + $event->setWidget($widget); } /** @@ -131,7 +139,7 @@ public function getEnvironment() * * @param PropertyInterface $property The property to get the widget class name for. * - * @return string + * @return class-string * * @SuppressWarnings(PHPMD.Superglobals) * @SuppressWarnings(PHPMD.CamelCaseVariableName) @@ -142,13 +150,11 @@ protected function getWidgetClass(PropertyInterface $property) return static::$widgetMapping[$property->getWidgetType()]; } - if (!isset($GLOBALS['BE_FFL'][$property->getWidgetType()])) { - return null; - } - - $className = $GLOBALS['BE_FFL'][$property->getWidgetType()]; + $className = $GLOBALS['BE_FFL'][$property->getWidgetType()] ?? ''; if (!\class_exists($className)) { - return null; + throw new DcGeneralRuntimeException( + \sprintf('Failed to get widget class for property "%s".', $property->getName()) + ); } return $className; @@ -160,7 +166,7 @@ protected function getWidgetClass(PropertyInterface $property) * @param PropertyInterface $propInfo The property for which the X label shall be generated. * @param ModelInterface $model The model. * - * @return array + * @return array|null */ protected function getOptionsForWidget($propInfo, $model): ?array { @@ -173,7 +179,11 @@ protected function getOptionsForWidget($propInfo, $model): ?array $event = new GetPropertyOptionsEvent($environment, $model); $event->setPropertyName($propInfo->getName()); $event->setOptions($options); - $environment->getEventDispatcher()->dispatch($event, GetPropertyOptionsEvent::NAME); + + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $dispatcher->dispatch($event, GetPropertyOptionsEvent::NAME); if ($event->getOptions() !== $options) { return $event->getOptions(); @@ -193,10 +203,9 @@ private function isGetOptionsAllowed(PropertyInterface $property): bool { $propExtra = $property->getExtra(); - // Check the overwrite param. + // Check to overwrite param. if ( - \is_array($propExtra) - && \array_key_exists('fetchOptions', $propExtra) + \array_key_exists('fetchOptions', $propExtra) && (true === $propExtra['fetchOptions']) ) { return true; @@ -219,10 +228,19 @@ private function isGetOptionsAllowed(PropertyInterface $property): bool protected function getTableWizard() { $environment = $this->getEnvironment(); + $dispatcher = $environment->getEventDispatcher(); - $defName = $environment->getDataDefinition()->getName(); - $translator = $environment->getTranslator(); - $urlEvent = new AddToUrlEvent('key=table'); + assert($dispatcher instanceof EventDispatcherInterface); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $defName = $definition->getName(); + + $translator = $environment->getTranslator(); + assert($translator instanceof TranslatorInterface); + + $urlEvent = new AddToUrlEvent('key=table'); $importTableEvent = new GenerateHtmlEvent( 'tablewizard.svg', @@ -261,9 +279,9 @@ protected function getTableWizard() ' %s %s%s', StringUtil::ampersand($urlEvent->getUrl()), StringUtil::specialchars($translator->translate('importTable.1', $defName)), - $importTableEvent->getHtml(), - $shrinkEvent->getHtml(), - $expandEvent->getHtml() + $importTableEvent->getHtml() ?? '', + $shrinkEvent->getHtml() ?? '', + $expandEvent->getHtml() ?? '' ); } @@ -275,9 +293,17 @@ protected function getTableWizard() protected function getListWizard() { $environment = $this->getEnvironment(); - $dispatcher = $environment->getEventDispatcher(); - $defName = $environment->getDataDefinition()->getName(); - $translator = $environment->getTranslator(); + + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $defName = $definition->getName(); + + $translator = $environment->getTranslator(); + assert($translator instanceof TranslatorInterface); $urlEvent = new AddToUrlEvent('key=list'); @@ -294,7 +320,7 @@ protected function getListWizard() ' %s', StringUtil::ampersand($urlEvent->getUrl()), StringUtil::specialchars($translator->translate('importList.1', $defName)), - $importListEvent->getHtml() + $importListEvent->getHtml() ?? '' ); } @@ -310,6 +336,7 @@ protected function getXLabel($propInfo) $xLabel = ''; $environment = $this->getEnvironment(); $translator = $environment->getTranslator(); + assert($translator instanceof TranslatorInterface); // Toggle line wrap (textarea). if (('textarea' === $propInfo->getWidgetType()) && !\array_key_exists('rte', $propInfo->getExtra())) { @@ -323,9 +350,12 @@ protected function getXLabel($propInfo) ) ); - $environment->getEventDispatcher()->dispatch($event, ContaoEvents::IMAGE_GET_HTML); + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $dispatcher->dispatch($event, ContaoEvents::IMAGE_GET_HTML); - $xLabel .= ' ' . $event->getHtml(); + $xLabel .= ' ' . ($event->getHtml() ?? ''); } $xLabel .= $this->getHelpWizard($propInfo); @@ -355,6 +385,8 @@ protected function getHelpWizard($propInfo) $helpWizard = ''; $environment = $this->getEnvironment(); $translator = $environment->getTranslator(); + assert($translator instanceof TranslatorInterface); + // Add the help wizard. if ($propInfo->getExtra() && \array_key_exists('helpwizard', $propInfo->getExtra())) { $event = new GenerateHtmlEvent( @@ -363,16 +395,22 @@ protected function getHelpWizard($propInfo) 'style="vertical-align:text-bottom;"' ); - $environment->getEventDispatcher()->dispatch($event, ContaoEvents::IMAGE_GET_HTML); + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $dispatcher->dispatch($event, ContaoEvents::IMAGE_GET_HTML); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); $helpWizard .= \sprintf( ' %s', - $environment->getDataDefinition()->getName(), + $definition->getName(), $propInfo->getName(), StringUtil::specialchars($translator->translate('MSC.helpWizard')), - $event->getHtml() + $event->getHtml() ?? '' ); } @@ -385,7 +423,7 @@ protected function getHelpWizard($propInfo) * @param PropertyInterface $property The property. * @param ModelInterface $model The current model. * - * @return Widget + * @return Widget|null * * @throws DcGeneralRuntimeException When not running in TL_MODE BE. * @@ -413,14 +451,18 @@ public function buildWidget( $prepareAttributes = $this->prepareWidgetAttributes($model, $property); $widget = new $class($prepareAttributes, new DcCompat($environment, $model, $property->getName())); + assert($widget instanceof Widget); // OH: what is this? source: DataContainer 232. $widget->currentRecord = $model->getId(); $widget->xlabel .= $this->getXLabel($property); + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + $event = new ManipulateWidgetEvent($environment, $model, $property, $widget); - $environment->getEventDispatcher()->dispatch($event, ManipulateWidgetEvent::NAME); + $dispatcher->dispatch($event, ManipulateWidgetEvent::NAME); return $widget; } @@ -442,7 +484,10 @@ private function valueToWidget(ModelInterface $model, PropertyInterface $propert ->setProperty($property->getName()) ->setValue($model->getProperty($property->getName())); - $environment->getEventDispatcher()->dispatch($event, $event::NAME); + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $dispatcher->dispatch($event, $event::NAME); $value = $event->getValue(); $propExtra = $property->getExtra(); @@ -472,11 +517,13 @@ private function valueToWidget(ModelInterface $model, PropertyInterface $propert private function prepareWidgetAttributes(ModelInterface $model, PropertyInterface $property) { $environment = $this->getEnvironment(); - $defName = $environment->getDataDefinition()->getName(); - $propExtra = $property->getExtra(); + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); - $value = $this->valueToWidget($model, $property); + $defName = $definition->getName(); + $propExtra = $property->getExtra(); + $value = $this->valueToWidget($model, $property); $propExtra['required'] = ('' === $value) && !empty($propExtra['mandatory']); @@ -505,7 +552,10 @@ private function prepareWidgetAttributes(ModelInterface $model, PropertyInterfac new DcCompat($environment, $model, $property->getName()) ); - $environment->getEventDispatcher()->dispatch($event, ContaoEvents::WIDGET_GET_ATTRIBUTES_FROM_DCA); + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $dispatcher->dispatch($event, ContaoEvents::WIDGET_GET_ATTRIBUTES_FROM_DCA); $prepareAttributes = $event->getResult(); if ( diff --git a/src/Contao/View/Contao2BackendView/TreePicker.php b/src/Contao/View/Contao2BackendView/TreePicker.php index d01ad303..ff8fd52e 100644 --- a/src/Contao/View/Contao2BackendView/TreePicker.php +++ b/src/Contao/View/Contao2BackendView/TreePicker.php @@ -28,6 +28,7 @@ use Contao\Backend; use Contao\CoreBundle\Exception\ResponseException; +use Contao\CoreBundle\Picker\PickerBuilderInterface; use Contao\CoreBundle\Picker\PickerConfig; use Contao\StringUtil; use Contao\System; @@ -36,7 +37,11 @@ use ContaoCommunityAlliance\Contao\Bindings\Events\Backend\AddToUrlEvent; use ContaoCommunityAlliance\Contao\Bindings\Events\Controller\ReloadEvent; use ContaoCommunityAlliance\Contao\Bindings\Events\Image\GenerateHtmlEvent; +use ContaoCommunityAlliance\DcGeneral\BaseConfigRegistryInterface; +use ContaoCommunityAlliance\DcGeneral\Contao\Compatibility\DcCompat; use ContaoCommunityAlliance\DcGeneral\Contao\DataDefinition\Definition\Contao2BackendViewDefinitionInterface; +use ContaoCommunityAlliance\DcGeneral\Contao\Factory\SessionStorageFactory; +use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminator; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\ModelToLabelEvent; use ContaoCommunityAlliance\DcGeneral\Controller\ModelCollector; use ContaoCommunityAlliance\DcGeneral\Controller\RelationshipManager; @@ -47,27 +52,40 @@ use ContaoCommunityAlliance\DcGeneral\Data\DCGE; use ContaoCommunityAlliance\DcGeneral\Data\ModelId; use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\BasicDefinitionInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\DefaultModelFormatterConfig; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\ListingConfigInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\ModelFormatterConfigInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\ModelRelationship\FilterBuilder; use ContaoCommunityAlliance\DcGeneral\DC\General; +use ContaoCommunityAlliance\DcGeneral\DcGeneral; use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralRuntimeException; use ContaoCommunityAlliance\DcGeneral\Factory\DcGeneralFactory; +use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; +use ContaoCommunityAlliance\DcGeneral\SessionStorageInterface; +use ContaoCommunityAlliance\Translator\TranslatorInterface; +use Symfony\Cmf\Component\Routing\ChainRouterInterface; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Generator\UrlGenerator; /** * Provide methods to handle input field "tableTree". * + * @property array{fields: list, format: string, maxCharacters: int}|null $itemLabel + * @property string $searchField + * * @SuppressWarnings(PHPMD.TooManyPublicMethods) * @SuppressWarnings(PHPMD.TooManyFields) * @SuppressWarnings(PHPMD.TooManyMethods) * @SuppressWarnings(PHPMD.ExcessiveClassLength) * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * @psalm-suppress PropertyNotSetInConstructor */ class TreePicker extends Widget { @@ -92,27 +110,6 @@ class TreePicker extends Widget */ protected $subTemplate = 'widget_treepicker'; - /** - * The ajax id. - * - * @var string - */ - protected $strAjaxId; - - /** - * The ajax key. - * - * @var string - */ - protected $strAjaxKey; - - /** - * The ajax name. - * - * @var string - */ - protected $strAjaxName; - /** * The data Container. * @@ -125,7 +122,7 @@ class TreePicker extends Widget * * @var string */ - protected $sourceName; + protected $sourceName = ''; /** * The field type to use. @@ -136,13 +133,6 @@ class TreePicker extends Widget */ protected $fieldType = 'radio'; - /** - * Flag determining if the value shall always be saved. - * - * @var bool - */ - protected $alwaysSave; - /** * The minimum level for items to be selectable. * @@ -169,12 +159,12 @@ class TreePicker extends Widget * * @var string */ - protected $title; + protected $title = ''; /** * The data container for the item source. * - * @var General + * @var DcGeneral */ protected $itemContainer; @@ -183,28 +173,28 @@ class TreePicker extends Widget * * @var string */ - protected $orderField; + protected $orderField = ''; /** * The tree nodes to be handled. * - * @var TreeNodeStates + * @var TreeNodeStates|null */ - protected $nodeStates; + protected $nodeStates = null; /** * Create a new instance. * - * @param array $attributes The custom attributes. - * @param General $dataContainer The data container. + * @param array $attributes The custom attributes. + * @param General|null $dataContainer The data container. * - * @throws DcGeneralRuntimeException When not in Backend mode. */ public function __construct($attributes = [], General $dataContainer = null) { parent::__construct($attributes); $scopeDeterminator = System::getContainer()->get('cca.dc-general.scope-matcher'); + assert($scopeDeterminator instanceof RequestScopeDeterminator); if ($scopeDeterminator->currentScopeIsUnknown() || !$scopeDeterminator->currentScopeIsBackend()) { throw new DcGeneralRuntimeException('Treepicker is currently for Backend only.'); @@ -216,42 +206,59 @@ public function __construct($attributes = [], General $dataContainer = null) /** * Setup all local values and create the dc instance for the referenced data source. * - * @param General $dataContainer The data container to use. + * @param General|null $dataContainer The data container to use. * * @return void */ protected function setUp(General $dataContainer = null) { - $this->dataContainer = $dataContainer ?: $this->objDca; + if (null === $dataContainer) { + /** @var General|null $dataContainer */ + $dataContainer = $this->objDca; + } - if (!$this->dataContainer) { + if (!$dataContainer) { return; } + $this->dataContainer = $dataContainer; + $environment = $this->dataContainer->getEnvironment(); if (!$this->sourceName) { - $property = $environment - ->getDataDefinition() + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + + $property = $definition ->getPropertiesDefinition() - ->getProperty($environment->getInputProvider()->getValue('name')); + ->getProperty($inputProvider->getValue('name')); foreach ($property->getExtra() as $k => $v) { $this->$k = $v; } - $name = $environment->getInputProvider()->getValue('name'); + $name = $inputProvider->getValue('name'); $this->strField = $name; $this->strName = $name; $this->strId = $name; $this->label = $property->getLabel() ?: $name; - $this->strTable = $environment->getDataDefinition()->getName(); + $this->strTable = $definition->getName(); } + $translator = $environment->getTranslator(); + assert($translator instanceof TranslatorInterface); + + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + /** @psalm-suppress PropertyNotSetInConstructor */ $this->itemContainer = (new DcGeneralFactory()) ->setContainerName($this->sourceName) - ->setTranslator($environment->getTranslator()) - ->setEventDispatcher($environment->getEventDispatcher()) + ->setTranslator($translator) + ->setEventDispatcher($dispatcher) ->createDcGeneral(); } @@ -279,10 +286,16 @@ public function updateAjax($ajaxAction, $dataContainer) $this->setUp($dataContainer); $environment = $this->dataContainer->getEnvironment(); - $this->value = $environment->getInputProvider()->getValue('value'); - $label = $environment - ->getDataDefinition() + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + + $this->value = $inputProvider->getValue('value'); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $label = $definition ->getPropertiesDefinition() ->getProperty($this->strName) ->getDescription(); @@ -292,7 +305,7 @@ public function updateAjax($ajaxAction, $dataContainer) $result = '' . '

' . $this->generate(); - if (null !== $label && $GLOBALS['TL_CONFIG']['showHelp']) { + if ($GLOBALS['TL_CONFIG']['showHelp']) { $result .= '

' . $label . '

'; } @@ -302,7 +315,7 @@ public function updateAjax($ajaxAction, $dataContainer) /** * Retrieve the item container. * - * @return General + * @return DcGeneral */ public function getItemContainer() { @@ -327,25 +340,35 @@ public function getEnvironment() public function getTreeNodeStates() { if (!isset($this->nodeStates)) { - $environment = $this->getEnvironment(); - $sessionStorage = $environment->getSessionStorage(); + $environment = $this->getEnvironment(); + + $sessionStorage = $environment->getSessionStorage(); + assert($sessionStorage instanceof SessionStorageInterface); + $this->nodeStates = new TreeNodeStates( $sessionStorage->get($this->getToggleId()), $this->determineParentsOfValues() ); + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + // Maybe it is not the best location to do this here. - if ('all' === $environment->getInputProvider()->getParameter('ptg')) { + if ('all' === $inputProvider->getParameter('ptg')) { // Save in session and reload. $sessionStorage->set( $this->getToggleId(), $this->nodeStates->setAllOpen($this->nodeStates->isAllOpen())->getStates() ); - $environment->getEventDispatcher()->dispatch(new ReloadEvent(), ContaoEvents::CONTROLLER_RELOAD); + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $dispatcher->dispatch(new ReloadEvent(), ContaoEvents::CONTROLLER_RELOAD); } } + /** @psalm-suppress PropertyNotSetInConstructor */ return $this->nodeStates; } @@ -397,9 +420,9 @@ public function __set($key, $value) /** * Convert the value according to the configured fieldtype. * - * @param mixed $value The value to convert. + * @param null|list|string $value The value to convert. * - * @return mixed + * @return null|string|list * * @throws \RuntimeException When an unknown field type is encountered. */ @@ -418,8 +441,10 @@ private function convertValue($value) return $value; case 'checkbox': $delimiter = (false !== \stripos($value, "\t")) ? "\t" : ','; + /** @var array $files */ + $files = StringUtil::trimsplit($delimiter, $value); - return StringUtil::trimsplit($delimiter, $value); + return \array_values($files); default: } throw new \RuntimeException('Unknown field type encountered: ' . $this->fieldType); @@ -434,10 +459,13 @@ private function convertValue($value) * * fieldType : the input type. Either "radio" or "checkbox". * * titleIcon: the icon to use in the title section. * * mandatory: the field value must not be empty. + * * orderField: the field to order items. * * @param string $key The property name. * - * @return string The property value. + * @return mixed The property value. + * + * @psalm-suppress LessSpecificImplementedReturnType */ public function __get($key) { @@ -452,7 +480,7 @@ public function __get($key) return $this->titleIcon; case 'mandatory': - return $this->arrConfiguration['mandatory'] ?? false; + return $this->arrConfiguration['mandatory'] ?? ''; case 'orderField': return $this->orderField; @@ -462,27 +490,32 @@ public function __get($key) default: } + return parent::__get($key); } /** * Skip the field if "change selection" is not checked. * - * @param array $value The current value. + * @param null|string|list $varInput The current value. + * + * @return null|string|list * - * @return array|string + * @psalm-suppress MoreSpecificImplementedParamType */ - protected function validator($value) + protected function validator($varInput) { - $convertValue = $this->widgetToValue($value); + $convertValue = $this->widgetToValue($varInput); if ((null === $convertValue) && $this->mandatory) { $translator = $this->getEnvironment()->getTranslator(); + assert($translator instanceof TranslatorInterface); $message = empty($this->label) ? $translator->translate('ERR.mdtryNoLabel') : \sprintf( $translator->translate('ERR.mandatory'), + /** @psalm-suppress PropertyNotSetInConstructor */ $this->strLabel ); @@ -501,8 +534,9 @@ protected function validator($value) */ public function renderItemsPlain() { - $values = []; - $value = $this->varValue; + $values = []; + $value = $this->varValue; + /** @psalm-suppress UndefinedThisPropertyFetch */ $idProperty = $this->idProperty ?: 'id'; if ('radio' === $this->fieldType && !empty($value)) { @@ -511,8 +545,14 @@ public function renderItemsPlain() if (\is_array($value) && !empty($value)) { $environment = $this->getEnvironment(); - $dataDriver = $environment->getDataProvider(); - $config = $environment->getBaseConfigRegistry()->getBaseConfig(); + + $dataDriver = $environment->getDataProvider(); + assert($dataDriver instanceof DataProviderInterface); + + $registry = $environment->getBaseConfigRegistry(); + assert($registry instanceof BaseConfigRegistryInterface); + + $config = $registry->getBaseConfig(); $filter = FilterBuilder::fromArrayForRoot() ->getFilter() ->andPropertyValueIn($idProperty, $value) @@ -526,6 +566,10 @@ public function renderItemsPlain() } foreach ($dataDriver->fetchAll($config) as $model) { + if (!($model instanceof ModelInterface)) { + continue; + } + $formatted = $this->formatModel($model, false); $idValue = $model->getProperty($idProperty); $values[$idValue] = $formatted[0]['content']; @@ -580,16 +624,25 @@ public function generate() $GLOBALS['TL_JAVASCRIPT']['cca.dc-general.vanillaGeneral'] = 'bundles/ccadcgeneral/js/vanillaGeneral.js'; $environment = $this->getEnvironment(); - $translator = $environment->getTranslator(); + + $translator = $environment->getTranslator(); + assert($translator instanceof TranslatorInterface); + $template = new ContaoBackendViewTemplate('widget_treepicker'); + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + $icon = new GenerateHtmlEvent($this->titleIcon); - $environment->getEventDispatcher()->dispatch($icon, ContaoEvents::IMAGE_GET_HTML); + $dispatcher->dispatch($icon, ContaoEvents::IMAGE_GET_HTML); $template ->setTranslator($translator) + /** @psalm-suppress PropertyNotSetInConstructor */ ->set('id', $this->strId) + /** @psalm-suppress PropertyNotSetInConstructor */ ->set('name', $this->strName) + /** @psalm-suppress PropertyNotSetInConstructor */ ->set('class', ($this->strClass ? ' ' . $this->strClass : '')) ->set('icon', $icon->getHtml()) ->set('title', $translator->translate($this->title ?: 'MSC.treePicker', null, [$this->sourceName])) @@ -618,14 +671,18 @@ public function generate() */ protected function generatePickerUrl() { + assert($this->dataContainer instanceof DcCompat); + $model = $this->dataContainer->getModel(); + assert($model instanceof ModelInterface); + $parameter = [ 'fieldType' => $this->fieldType, 'sourceName' => $this->sourceName, - 'modelId' => ModelId::fromModel($this->dataContainer->getModel())->getSerialized(), + 'modelId' => ModelId::fromModel($model)->getSerialized(), 'orderField' => $this->orderField, 'propertyName' => $this->name ]; - + /** @psalm-suppress UndefinedThisPropertyFetch */ if ($this->pickerOrderProperty && $this->pickerSortDirection) { $parameter = \array_merge( $parameter, @@ -636,15 +693,18 @@ protected function generatePickerUrl() ); } - return System::getContainer()->get('contao.picker.builder')->getUrl('cca_tree', $parameter); + $pickerBuilder = System::getContainer()->get('contao.picker.builder'); + assert($pickerBuilder instanceof PickerBuilderInterface); + + return $pickerBuilder->getUrl('cca_tree', $parameter); } /** * Convert the value from widget for internal process. * - * @param mixed $value The widget value. + * @param null|string|list $value The widget value. * - * @return null|string|array + * @return null|string|list */ public function widgetToValue($value) { @@ -680,21 +740,31 @@ public function valueToWidget($value) */ protected function generateUpdateUrl() { - $request = System::getContainer()->get('request_stack')->getCurrentRequest(); + $requestStack = System::getContainer()->get('request_stack'); + assert($requestStack instanceof RequestStack); + + $request = $requestStack->getCurrentRequest(); + assert($request instanceof Request); + + $dataContainer = $this->dataContainer; + assert($dataContainer instanceof General); $configPicker = new PickerConfig( 'cca_tree', [ 'fieldType' => $this->fieldType, 'sourceName' => $this->sourceName, - 'modelId' => ModelId::fromModel($this->dataContainer->getModel())->getSerialized(), + 'modelId' => ModelId::fromModel($dataContainer->getModel())->getSerialized(), 'orderField' => $this->orderField, 'propertyName' => $this->name ], $this->valueToWidget($this->value) ); - return System::getContainer()->get('router')->generate( + $router = System::getContainer()->get('router'); + assert($router instanceof ChainRouterInterface); + + return $router->generate( 'cca_dc_general_tree_update', [ 'picker' => $configPicker->cloneForCurrent((string) $request->query->get('context'))->urlEncode() @@ -717,9 +787,16 @@ private function generateBreadCrumbUrl(ModelInterface $model) $toggleUrlEvent = new AddToUrlEvent( 'ptg=' . $model->getId() . '&provider=' . $model->getProviderName() ); - $this->getEnvironment()->getEventDispatcher()->dispatch($toggleUrlEvent, ContaoEvents::BACKEND_ADD_TO_URL); - return System::getContainer()->get('router')->generate( + $dispatcher = $this->getEnvironment()->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $dispatcher->dispatch($toggleUrlEvent, ContaoEvents::BACKEND_ADD_TO_URL); + + $router = System::getContainer()->get('router'); + assert($router instanceof ChainRouterInterface); + + return $router->generate( 'cca_dc_general_tree_breadcrumb', $this->getQueryParameterFromUrl($toggleUrlEvent->getUrl()), UrlGenerator::ABSOLUTE_URL @@ -738,9 +815,16 @@ private function generateToggleUrl(ModelInterface $model) $toggleUrlEvent = new AddToUrlEvent( 'ptg=' . $model->getId() . '&provider=' . $model->getProviderName() ); - $this->getEnvironment()->getEventDispatcher()->dispatch($toggleUrlEvent, ContaoEvents::BACKEND_ADD_TO_URL); - return System::getContainer()->get('router')->generate( + $dispatcher = $this->getEnvironment()->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $dispatcher->dispatch($toggleUrlEvent, ContaoEvents::BACKEND_ADD_TO_URL); + + $router = System::getContainer()->get('router'); + assert($router instanceof ChainRouterInterface); + + return $router->generate( 'cca_dc_general_tree_toggle', $this->getQueryParameterFromUrl($toggleUrlEvent->getUrl()), UrlGenerator::ABSOLUTE_URL @@ -756,7 +840,7 @@ private function generateToggleUrl(ModelInterface $model) */ private function getQueryParameterFromUrl($url) { - $parameters = array(); + $parameters = []; foreach (\preg_split('/&(amp;)?/i', \preg_split('/[?]/ui', $url)[1]) as $value) { $chunks = \explode('=', $value); $parameters[$chunks[0]] = $chunks[1]; @@ -779,7 +863,9 @@ private function addOrderFieldToTemplate(ContaoBackendViewTemplate $template) } $translator = $this->getEnvironment()->getTranslator(); + assert($translator instanceof TranslatorInterface); + /** @psalm-suppress UndefinedThisPropertyFetch */ $template ->set('hasOrder', true) ->set('orderId', $this->orderField) @@ -802,16 +888,25 @@ public function generatePopup() $GLOBALS['TL_JAVASCRIPT']['cca.dc-general.vanillaGeneral'] = 'bundles/ccadcgeneral/js/vanillaGeneral.js'; $environment = $this->getEnvironment(); - $translator = $environment->getTranslator(); - $template = new ContaoBackendViewTemplate('widget_treepicker_popup'); + + $translator = $environment->getTranslator(); + assert($translator instanceof TranslatorInterface); + + $template = new ContaoBackendViewTemplate('widget_treepicker_popup'); $icon = new GenerateHtmlEvent($this->titleIcon); - $environment->getEventDispatcher()->dispatch($icon, ContaoEvents::IMAGE_GET_HTML); + + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $dispatcher->dispatch($icon, ContaoEvents::IMAGE_GET_HTML); $template ->setTranslator($translator) ->set('id', 'tl_listing') + /** @psalm-suppress PropertyNotSetInConstructor */ ->set('name', $this->strName) + /** @psalm-suppress PropertyNotSetInConstructor */ ->set('class', ($this->strClass ? ' ' . $this->strClass : '')) ->set('icon', $icon->getHtml()) ->set('title', $translator->translate($this->title ?: 'MSC.treePicker', null, [$this->sourceName])) @@ -841,6 +936,7 @@ public function generatePopup() */ private function getRootIds() { + /** @psalm-suppress UndefinedThisPropertyFetch */ $root = $this->root; $root = \is_array($root) ? $root : ((\is_numeric($root) && $root > 0) ? [$root] : []); $root = \array_merge($root, [0]); @@ -856,15 +952,22 @@ private function getRootIds() public function generateAjax() { $input = $this->getEnvironment()->getInputProvider(); + assert($input instanceof InputProviderInterface); if ($input->hasValue('action') && ('DcGeneralLoadSubTree' === $input->getValue('action'))) { $provider = $input->getValue('providerName'); $rootId = $input->getValue('id'); - $this->getEnvironment()->getSessionStorage()->set( + + $sessionStorage = $this->getEnvironment()->getSessionStorage(); + assert($sessionStorage instanceof SessionStorageInterface); + + $sessionStorage->set( $this->getToggleId(), $this->getTreeNodeStates()->toggleModel($provider, $rootId)->getStates() ); + $collection = $this->loadCollection($rootId, ((int) $input->getValue('level') + 1)); + return $this->generateTreeView($collection, 'tree'); } @@ -878,7 +981,10 @@ public function generateAjax() */ protected function getToggleId() { - return $this->getEnvironment()->getDataDefinition()->getName() . $this->strId . '_tree'; + $definition = $this->getEnvironment()->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + return $definition->getName() . $this->strId . '_tree'; } /** @@ -888,7 +994,10 @@ protected function getToggleId() */ public function getSearchSessionKey() { - return $this->getEnvironment()->getDataDefinition()->getName() . $this->strId . '_tree_search'; + $definition = $this->getEnvironment()->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + return $definition->getName() . $this->strId . '_tree_search'; } /** @@ -932,8 +1041,12 @@ protected function determineModelState(ModelInterface $model, $level) */ protected function treeWalkModel(ModelInterface $model, $level, $subTables = []) { - $environment = $this->getEnvironment(); - $relationships = $environment->getDataDefinition()->getModelRelationshipDefinition(); + $environment = $this->getEnvironment(); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $relationships = $definition->getModelRelationshipDefinition(); $hasChild = false; $this->determineModelState($model, ($level - 1)); @@ -949,12 +1062,15 @@ protected function treeWalkModel(ModelInterface $model, $level, $subTables = []) } // Create a new Config and fetch the children from the child provider. - $childConfig = $environment->getDataProvider($subTable)->getEmptyConfig(); + $dataProvider = $environment->getDataProvider($subTable); + assert($dataProvider instanceof DataProviderInterface); + + $childConfig = $dataProvider->getEmptyConfig(); $childConfig->setFilter($childFilter->getFilter($model)); $childConfig->setSorting(['sorting' => 'ASC']); - - $childCollection = $environment->getDataProvider($subTable)->fetchAll($childConfig); + $childCollection = $dataProvider->fetchAll($childConfig); + assert($childCollection instanceof CollectionInterface); $hasChild = ($childCollection->length() > 0); @@ -994,10 +1110,13 @@ protected function treeWalkModel(ModelInterface $model, $level, $subTables = []) */ private function treeWalkChildCollection(CollectionInterface $childCollection, ModelInterface $model, $level) { - $relationships = $this->getEnvironment()->getDataDefinition()->getModelRelationshipDefinition(); + $definition = $this->getEnvironment()->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $relationships = $definition->getModelRelationshipDefinition(); foreach ($childCollection as $childModel) { - // Let the child know about it's parent. + // Let the child know about its parent. $model->setMeta($model::PARENT_ID, $model->getId()); $model->setMeta($model::PARENT_PROVIDER_NAME, $model->getProviderName()); @@ -1013,21 +1132,32 @@ private function treeWalkChildCollection(CollectionInterface $childCollection, M /** * Recursively retrieve a collection of all complete node hierarchy. * - * @param array $rootId The ids of the root node. - * @param int $level The level the items are residing on. - * @param string $providerName The data provider from which the root element originates from. + * @param mixed|null $rootId The ids of the root node. + * @param int $level The level the items are residing on. + * @param string $providerName The data provider from which the root element originates from. * * @return CollectionInterface */ public function getTreeCollectionRecursive($rootId, $level = 0, $providerName = null) { - $environment = $this->getEnvironment(); - $dataDriver = $environment->getDataProvider($providerName); + $environment = $this->getEnvironment(); + + $dataDriver = $environment->getDataProvider($providerName); + assert($dataDriver instanceof DataProviderInterface); + $tableTreeData = $dataDriver->getEmptyCollection(); - $rootConfig = $environment->getBaseConfigRegistry()->getBaseConfig(); - $relationships = $environment->getDataDefinition()->getModelRelationshipDefinition(); - if (!$rootId) { + $registry = $environment->getBaseConfigRegistry(); + assert($registry instanceof BaseConfigRegistryInterface); + + $rootConfig = $registry->getBaseConfig(); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $relationships = $definition->getModelRelationshipDefinition(); + + if (null === $rootId) { $this->prepareFilterForRootCondition(); $this->pushRootModelToTreeCollection($dataDriver, $tableTreeData, $level); @@ -1037,6 +1167,7 @@ public function getTreeCollectionRecursive($rootId, $level = 0, $providerName = $rootConfig->setId($rootId); // Fetch root element. $rootModel = $dataDriver->fetch($rootConfig); + assert($rootModel instanceof ModelInterface); $mySubTables = []; foreach ($relationships->getChildConditions($rootModel->getProviderName()) as $condition) { @@ -1057,10 +1188,17 @@ public function getTreeCollectionRecursive($rootId, $level = 0, $providerName = */ private function prepareFilterForRootCondition() { - $environment = $this->getEnvironment(); - $rootCondition = $environment->getDataDefinition()->getModelRelationshipDefinition()->getRootCondition(); + $environment = $this->getEnvironment(); - $baseConfig = $environment->getBaseConfigRegistry()->getBaseConfig(); + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $rootCondition = $definition->getModelRelationshipDefinition()->getRootCondition(); + + $registry = $environment->getBaseConfigRegistry(); + assert($registry instanceof BaseConfigRegistryInterface); + + $baseConfig = $registry->getBaseConfig(); if (!$rootCondition) { return $baseConfig; } @@ -1089,12 +1227,19 @@ private function prepareFilterForRootCondition() private function pushRootModelToTreeCollection( DataProviderInterface $dataProvider, CollectionInterface $treeCollection, - $level + int $level ) { - $environment = $this->getEnvironment(); + $environment = $this->getEnvironment(); + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + $baseConfig = $this->prepareFilterForRootCondition(); - $relationships = $environment->getDataDefinition()->getModelRelationshipDefinition(); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $relationships = $definition->getModelRelationshipDefinition(); if ($inputProvider->hasParameter('orderProperty') && $inputProvider->hasParameter('sortDirection')) { $orderProperty = $inputProvider->getParameter('orderProperty'); @@ -1105,12 +1250,17 @@ private function pushRootModelToTreeCollection( // Fetch all root elements. $collection = $dataProvider->fetchAll($baseConfig); + assert($collection instanceof CollectionInterface); + if (!$collection->count()) { return; } + $firstModel = $collection->get(0); + assert($firstModel instanceof ModelInterface); + $mySubTables = []; - foreach ($relationships->getChildConditions($collection->get(0)->getProviderName()) as $condition) { + foreach ($relationships->getChildConditions($firstModel->getProviderName()) as $condition) { $mySubTables[] = $condition->getDestinationName(); } @@ -1124,9 +1274,9 @@ private function pushRootModelToTreeCollection( /** * Load the collection of child items and the parent item for the currently selected parent item. * - * @param mixed $rootId The root element (or null to fetch everything). - * @param int $level The current level in the tree (of the optional root element). - * @param null $providerName The data provider from which the optional root element shall be taken from. + * @param mixed|null $rootId The root element (or null to fetch everything). + * @param int $level The current level in the tree (of the optional root element). + * @param null $providerName The data provider from which the optional root element shall be taken from. * * @return CollectionInterface */ @@ -1136,9 +1286,14 @@ public function loadCollection($rootId = null, $level = 0, $providerName = null) $collection = $this->getTreeCollectionRecursive($rootId, $level, $providerName); - if ($rootId) { - $treeData = $environment->getDataProvider($providerName)->getEmptyCollection(); + if (null !== $rootId) { + $dataProvider = $environment->getDataProvider($providerName); + assert($dataProvider instanceof DataProviderInterface); + + $treeData = $dataProvider->getEmptyCollection(); $model = $collection->get(0); + assert($model instanceof ModelInterface); + foreach ($model->getMeta($model::CHILD_COLLECTIONS) as $collection) { foreach ($collection as $subModel) { $treeData->push($subModel); @@ -1162,7 +1317,12 @@ protected function getFormatter(ModelInterface $model, $treeMode) { /** @var ListingConfigInterface $listing */ $definition = $this->getEnvironment()->getDataDefinition(); - $listing = $definition->getDefinition(Contao2BackendViewDefinitionInterface::NAME)->getListingConfig(); + assert($definition instanceof ContainerInterface); + + $backendView = $definition->getDefinition(Contao2BackendViewDefinitionInterface::NAME); + assert($backendView instanceof Contao2BackendViewDefinitionInterface); + + $listing = $backendView->getListingConfig(); if ($listing->hasLabelFormatter($model->getProviderName())) { return $listing->getLabelFormatter($model->getProviderName()); @@ -1170,7 +1330,7 @@ protected function getFormatter(ModelInterface $model, $treeMode) // If not in tree mode and custom label has been defined, use it. if (!$treeMode && $this->itemLabel) { - $label = (array) $this->itemLabel; + $label = $this->itemLabel; $formatter = new DefaultModelFormatterConfig(); $formatter->setPropertyNames($label['fields']); $formatter->setFormat($label['format']); @@ -1204,13 +1364,18 @@ protected function getFormatter(ModelInterface $model, $treeMode) public function formatModel(ModelInterface $model, $treeMode = true) { /** @var ListingConfigInterface $listing */ - $environment = $this->getEnvironment(); - $definition = $environment->getDataDefinition(); - $listing = $definition - ->getDefinition(Contao2BackendViewDefinitionInterface::NAME) - ->getListingConfig(); - $properties = $definition->getPropertiesDefinition(); - $defaultSortFields = \array_keys((array) $listing->getDefaultSortingFields()); + $environment = $this->getEnvironment(); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $backendView = $definition->getDefinition(Contao2BackendViewDefinitionInterface::NAME); + assert($backendView instanceof Contao2BackendViewDefinitionInterface); + + $listing = $backendView->getListingConfig(); + $properties = $definition->getPropertiesDefinition(); + /** @psalm-suppress DeprecatedMethod */ + $defaultSortFields = \array_keys($listing->getDefaultSortingFields()); $firstSorting = \reset($defaultSortFields); $formatter = $this->getFormatter($model, $treeMode); @@ -1229,7 +1394,10 @@ public function formatModel(ModelInterface $model, $treeMode = true) ->setLabel($formatter->getFormat()) ->setFormatter($formatter); - $environment->getEventDispatcher()->dispatch($event, $event::NAME); + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $dispatcher->dispatch($event, $event::NAME); $labelList = []; $this->prepareLabelWithDisplayedProperties($formatter, $event->getArgs(), $firstSorting, $labelList); @@ -1243,7 +1411,7 @@ public function formatModel(ModelInterface $model, $treeMode = true) * * @param ModelFormatterConfigInterface $formatter The model formatter. * @param array $arguments The model label arguments. - * @param string|bool $firstSorting The first sorting. + * @param bool|string $firstSorting The first sorting. * @param array $labelList The label list. * * @return void @@ -1251,11 +1419,16 @@ public function formatModel(ModelInterface $model, $treeMode = true) private function prepareLabelWithDisplayedProperties( ModelFormatterConfigInterface $formatter, array $arguments, - $firstSorting, + bool|string $firstSorting, array &$labelList ) { $definition = $this->getEnvironment()->getDataDefinition(); - $listing = $definition->getDefinition(Contao2BackendViewDefinitionInterface::NAME)->getListingConfig(); + assert($definition instanceof ContainerInterface); + + $backendView = $definition->getDefinition(Contao2BackendViewDefinitionInterface::NAME); + assert($backendView instanceof Contao2BackendViewDefinitionInterface); + + $listing = $backendView->getListingConfig(); if (!$listing->getShowColumns()) { return; } @@ -1284,18 +1457,23 @@ private function prepareLabelWithDisplayedProperties( private function prepareLabelWithOutDisplayedProperties( ModelFormatterConfigInterface $formatter, array $arguments, - $label, + string $label, array &$labelList ) { $definition = $this->getEnvironment()->getDataDefinition(); - $listing = $definition->getDefinition(Contao2BackendViewDefinitionInterface::NAME)->getListingConfig(); + assert($definition instanceof ContainerInterface); + + $backendView = $definition->getDefinition(Contao2BackendViewDefinitionInterface::NAME); + assert($backendView instanceof Contao2BackendViewDefinitionInterface); + + $listing = $backendView->getListingConfig(); if ($listing->getShowColumns()) { return; } $string = \vsprintf($label, $arguments); - if (($maxLength = null !== $formatter->getMaxLength()) && \strlen($string) > $maxLength) { + if ((null !== $maxLength = $formatter->getMaxLength()) && \strlen($string) > $maxLength) { $string = \substr($string, 0, $maxLength); } @@ -1318,10 +1496,13 @@ protected function parseModel($model, $toggleID) { $model->setMeta($model::LABEL_VALUE, $this->formatModel($model)); + $translator = $this->getEnvironment()->getTranslator(); + assert($translator instanceof TranslatorInterface); + if ($model->getMeta($model::SHOW_CHILDREN)) { - $toggleTitle = $this->getEnvironment()->getTranslator()->translate('MSC.collapseNode'); + $toggleTitle = $translator->translate('MSC.collapseNode'); } else { - $toggleTitle = $this->getEnvironment()->getTranslator()->translate('MSC.expandNode'); + $toggleTitle = $translator->translate('MSC.expandNode'); } $toggleScript = \sprintf( @@ -1335,8 +1516,9 @@ protected function parseModel($model, $toggleID) ); $template = new ContaoBackendViewTemplate('widget_treepicker_entry'); + /** @psalm-suppress UndefinedThisPropertyFetch */ $template - ->setTranslator($this->getEnvironment()->getTranslator()) + ->setTranslator($translator) ->set('id', $this->strId) ->set('name', $this->strName) ->set('theme', Backend::getTheme()) @@ -1387,8 +1569,11 @@ protected function generateTreeView($collection, $treeClass) $subHtml .= $this->generateTreeView($objChildCollection, $treeClass); } + $translator = $this->getEnvironment()->getTranslator(); + assert($translator instanceof TranslatorInterface); + $template - ->setTranslator($this->getEnvironment()->getTranslator()) + ->setTranslator($translator) ->set('objParentModel', $model) ->set('strToggleID', $toggleID) ->set('strHTML', $subHtml) @@ -1406,22 +1591,30 @@ protected function generateTreeView($collection, $treeClass) * Fetch all parents of the passed model. * * @param ModelInterface $model The model. - * @param string[] $parents The ids of all detected parents so far. + * @param array $parents The ids of all detected parents so far. * * @return void */ private function parentsOf($model, &$parents) { - $environment = $this->getEnvironment(); + $environment = $this->getEnvironment(); + $dataDefinition = $environment->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + + $mode = $dataDefinition->getBasicDefinition()->getMode(); + assert(\is_int($mode)); + $collector = new ModelCollector($this->getEnvironment()); $relationships = new RelationshipManager( $dataDefinition->getModelRelationshipDefinition(), - $dataDefinition->getBasicDefinition()->getMode() + $mode ); if (!$relationships->isRoot($model)) { $parent = $collector->searchParentOf($model); + assert($parent instanceof ModelInterface); + if (!isset($parents[$model->getProviderName()][$parent->getId()])) { $this->parentsOf($parent, $parents); } @@ -1439,14 +1632,24 @@ private function determineParentsOfValues() { $parents = []; $environment = $this->getEnvironment(); - $mode = $environment->getDataDefinition()->getBasicDefinition()->getMode(); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $mode = $definition->getBasicDefinition()->getMode(); + if (BasicDefinitionInterface::MODE_HIERARCHICAL !== $mode) { return []; } foreach ((array) $this->varValue as $value) { $dataDriver = $environment->getDataProvider(); - $this->parentsOf($dataDriver->fetch($dataDriver->getEmptyConfig()->setId($value)), $parents); + assert($dataDriver instanceof DataProviderInterface); + + $model = $dataDriver->fetch($dataDriver->getEmptyConfig()->setId($value)); + assert($model instanceof ModelInterface); + + $this->parentsOf($model, $parents); } return $parents; @@ -1459,31 +1662,41 @@ private function determineParentsOfValues() */ private function handleInputNameForEditAll() { + $inputProvider = $this->getEnvironment()->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + if ( - ('select' !== $this->getEnvironment()->getInputProvider()->getParameter('act')) - && ('edit' !== $this->getEnvironment()->getInputProvider()->getParameter('select')) - && ('edit' !== $this->getEnvironment()->getInputProvider()->getParameter('mode')) + ('select' !== $inputProvider->getParameter('act')) + && ('edit' !== $inputProvider->getParameter('select')) + && ('edit' !== $inputProvider->getParameter('mode')) ) { return; } - $tableName = \explode('____', $this->getEnvironment()->getInputProvider()->getValue('name'))[0]; + $tableName = \explode('____', $inputProvider->getValue('name'))[0]; $sessionKey = 'DC_GENERAL_' . \strtoupper($tableName); - $sessionStorage = - System::getContainer()->get('cca.dc-general.session_factory')->createService()->setScope($sessionKey); + $sessionFactory = System::getContainer()->get('cca.dc-general.session_factory'); + assert($sessionFactory instanceof SessionStorageFactory); - $selectAction = $this->getEnvironment()->getInputProvider()->getParameter('select'); + $sessionStorage = $sessionFactory->createService(); + assert($sessionStorage instanceof SessionStorageInterface); + $sessionStorage->setScope($sessionKey); + $selectAction = $inputProvider->getParameter('select'); + + /** @var array{models: list} $session */ $session = $sessionStorage->get($tableName . '.' . $selectAction); + $propertyNamePrefix = ''; $originalPropertyName = null; - foreach ((array) $session['models'] as $modelId) { + + foreach ($session['models'] as $modelId) { if (null !== $originalPropertyName) { break; } $propertyNamePrefix = \str_replace('::', '____', $modelId) . '_'; - if (0 !== strpos($this->strName, $propertyNamePrefix)) { + if (0 !== \strpos($this->strName, $propertyNamePrefix)) { continue; } diff --git a/src/Contao/View/Contao2BackendView/TreeSelect.php b/src/Contao/View/Contao2BackendView/TreeSelect.php index f67c0f88..2a752d70 100644 --- a/src/Contao/View/Contao2BackendView/TreeSelect.php +++ b/src/Contao/View/Contao2BackendView/TreeSelect.php @@ -33,11 +33,17 @@ use Contao\System; use ContaoCommunityAlliance\DcGeneral\Contao\Compatibility\DcCompat; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Subscriber\WidgetBuilder; +use ContaoCommunityAlliance\DcGeneral\Data\DataProviderInterface; use ContaoCommunityAlliance\DcGeneral\Data\ModelId; +use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\DcGeneral; use ContaoCommunityAlliance\DcGeneral\Factory\DcGeneralFactory; +use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; +use ContaoCommunityAlliance\DcGeneral\SessionStorageInterface; use ContaoCommunityAlliance\Translator\Contao\LangArrayTranslator; use ContaoCommunityAlliance\Translator\TranslatorChain; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; /** * Class TreeSelect. @@ -45,16 +51,13 @@ * Back end tree picker for usage in generaltree.php. * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * + * @deprecated Do not use - here for legacy reasons only. + * + * @psalm-suppress PropertyNotSetInConstructor */ class TreeSelect { - /** - * Current ajax object. - * - * @var object - */ - protected $objAjax; - /** * The DcGeneral Object. * @@ -78,9 +81,11 @@ public function __construct() Config::getInstance(); Database::getInstance(); + /** @psalm-suppress DeprecatedMethod */ BackendUser::getInstance()->authenticate(); System::loadLanguageFile('default'); + /** @psalm-suppress DeprecatedMethod */ Backend::setStaticUrls(); } @@ -89,14 +94,20 @@ public function __construct() * * @return void * + * @throws \Exception * @SuppressWarnings(PHPMD.Superglobals) * @SuppressWarnings(PHPMD.CamelCaseVariableName) + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ public function run() { - $environment = $this->itemContainer->getEnvironment(); + $environment = $this->itemContainer->getEnvironment(); + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + $sessionStorage = $environment->getSessionStorage(); + assert($sessionStorage instanceof SessionStorageInterface); // Ajax request. // @codingStandardsIgnoreStart - We need POST access here. @@ -114,6 +125,7 @@ public function run() \define('CURRENT_ID', ($inputTable ? $sessionStorage->get('CURRENT_ID') : $inputId)); $dispatcher = System::getContainer()->get('event_dispatcher'); + assert($dispatcher instanceof EventDispatcherInterface); $translator = new TranslatorChain(); $translator->add(new LangArrayTranslator($dispatcher)); @@ -131,8 +143,10 @@ public function run() } // Merge with the information from the data container. - $property = $environment - ->getDataDefinition() + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $property = $definition ->getPropertiesDefinition() ->getProperty($inputField); $extra = $property->getExtra(); @@ -142,14 +156,19 @@ public function run() $property->setExtra(\array_merge($property->getExtra(), $information['eval'])); $dataProvider = $environment->getDataProvider(); - $model = $dataProvider->getEmptyModel(); + assert($dataProvider instanceof DataProviderInterface); + + $model = $dataProvider->getEmptyModel(); if ($inputProvider->getParameter('id')) { $modelId = ModelId::fromSerialized($inputProvider->getParameter('id')); $model = $dataProvider->fetch($dataProvider->getEmptyConfig()->setId($modelId->getId())); + assert($model instanceof ModelInterface); } + $widgetBuilder = new WidgetBuilder($environment); + /** @var \ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\TreePicker $treeSelector */ - $treeSelector = (new WidgetBuilder($environment))->buildWidget($property, $model); + $treeSelector = $widgetBuilder->buildWidget($property, $model); $treeSelector->value = \array_filter(\explode(',', $inputProvider->getParameter('value'))); // AJAX request. @@ -160,6 +179,7 @@ public function run() $template = new ContaoBackendViewTemplate('be_main'); + /** @psalm-suppress UndefinedMagicPropertyFetch */ $template ->set('isPopup', true) ->set('main', $treeSelector->generatePopup()) @@ -177,6 +197,10 @@ public function run() ->set('managerHref', ''); // Add the manager link. + /** + * @psalm-suppress UndefinedThisPropertyFetch + * @psalm-suppress UndefinedMagicPropertyFetch + */ if ($treeSelector->managerHref) { $template ->set('managerHref', 'contao?' . StringUtil::ampersand($treeSelector->managerHref) . '&popup=1'); diff --git a/src/Contao/View/Contao2BackendView/TreeView.php b/src/Contao/View/Contao2BackendView/TreeView.php index 3a05c6fb..9f143ecf 100644 --- a/src/Contao/View/Contao2BackendView/TreeView.php +++ b/src/Contao/View/Contao2BackendView/TreeView.php @@ -32,16 +32,21 @@ use ContaoCommunityAlliance\Contao\Bindings\Events\Backend\AddToUrlEvent; use ContaoCommunityAlliance\Contao\Bindings\Events\Image\GenerateHtmlEvent; use ContaoCommunityAlliance\DcGeneral\Action; +use ContaoCommunityAlliance\DcGeneral\Clipboard\ClipboardInterface; use ContaoCommunityAlliance\DcGeneral\Clipboard\Filter; use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminator; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\GetPasteRootButtonEvent; +use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\ModelToLabelEvent; +use ContaoCommunityAlliance\DcGeneral\Controller\ControllerInterface; use ContaoCommunityAlliance\DcGeneral\Controller\ModelCollector; use ContaoCommunityAlliance\DcGeneral\Controller\TreeCollector; use ContaoCommunityAlliance\DcGeneral\Controller\TreeNodeStates; use ContaoCommunityAlliance\DcGeneral\Data\CollectionInterface; +use ContaoCommunityAlliance\DcGeneral\Data\DataProviderInterface; use ContaoCommunityAlliance\DcGeneral\Data\ModelId; use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; use ContaoCommunityAlliance\DcGeneral\Data\MultiLanguageDataProviderInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\DcGeneralEvents; use ContaoCommunityAlliance\DcGeneral\DcGeneralViews; use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; @@ -50,8 +55,13 @@ use ContaoCommunityAlliance\DcGeneral\Event\ViewEvent; use ContaoCommunityAlliance\DcGeneral\EventListener\ModelRelationship\TreeEnforcingListener; use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralRuntimeException; +use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; use ContaoCommunityAlliance\DcGeneral\Panel\LimitElementInterface; +use ContaoCommunityAlliance\DcGeneral\Panel\PanelContainerInterface; use ContaoCommunityAlliance\DcGeneral\Panel\SortElementInterface; +use ContaoCommunityAlliance\DcGeneral\SessionStorageInterface; +use ContaoCommunityAlliance\Translator\TranslatorInterface; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface; @@ -94,6 +104,8 @@ public function __construct( if (null === $tokenManager) { $tokenManager = System::getContainer()->get('security.csrf.token_manager'); + assert($tokenManager instanceof CsrfTokenManagerInterface); + // @codingStandardsIgnoreStart @trigger_error( 'Not passing the csrf token manager as 2th argument to "' . __METHOD__ . '" is deprecated ' . @@ -104,6 +116,8 @@ public function __construct( } if (null === $tokenName) { $tokenName = System::getContainer()->getParameter('contao.csrf_token_name'); + assert(\is_string($tokenName)); + // @codingStandardsIgnoreStart @trigger_error( 'Not passing the csrf token name as 3th argument to "' . __METHOD__ . '" is deprecated ' . @@ -134,8 +148,13 @@ protected function getToggleId() */ protected function getTreeNodeStates() { - $sessionStorage = $this->getEnvironment()->getSessionStorage(); - $openElements = $sessionStorage->get($this->getToggleId()); + $environment = $this->getEnvironment(); + assert($environment instanceof EnvironmentInterface); + + $sessionStorage = $environment->getSessionStorage(); + assert($sessionStorage instanceof SessionStorageInterface); + + $openElements = $sessionStorage->get($this->getToggleId()); if (!\is_array($openElements)) { $openElements = []; @@ -153,7 +172,12 @@ protected function getTreeNodeStates() */ protected function saveTreeNodeStates(TreeNodeStates $states) { - $sessionStorage = $this->getEnvironment()->getSessionStorage(); + $environment = $this->getEnvironment(); + assert($environment instanceof EnvironmentInterface); + + $sessionStorage = $environment->getSessionStorage(); + assert($sessionStorage instanceof SessionStorageInterface); + $sessionStorage->set($this->getToggleId(), $states->getStates()); } @@ -166,7 +190,12 @@ protected function saveTreeNodeStates(TreeNodeStates $states) */ private function handleNodeStateChanges() { - $input = $this->getEnvironment()->getInputProvider(); + $environment = $this->getEnvironment(); + assert($environment instanceof EnvironmentInterface); + + $input = $environment->getInputProvider(); + assert($input instanceof InputProviderInterface); + if (($modelId = $input->getParameter('ptg')) && ($providerName = $input->getParameter('provider'))) { $states = $this->getTreeNodeStates(); // Check if the open/close all has been triggered or just a model. @@ -179,7 +208,7 @@ private function handleNodeStateChanges() $this->toggleModel($providerName, $modelId); } - ViewHelpers::redirectHome($this->environment); + ViewHelpers::redirectHome($environment); } } @@ -219,27 +248,40 @@ protected function isModelOpen($model) */ public function loadCollection($rootId = null, $level = 0, $providerName = null) { - $environment = $this->getEnvironment(); - $dataDriver = $environment->getDataProvider($providerName); + $environment = $this->getEnvironment(); + assert($environment instanceof EnvironmentInterface); + + $dataDriver = $environment->getDataProvider($providerName); + assert($dataDriver instanceof DataProviderInterface); + + $panel = $this->getPanel(); + assert($panel instanceof PanelContainerInterface); + $realProvider = $dataDriver->getEmptyModel()->getProviderName(); - $collector = new TreeCollector( + + /** @psalm-suppress DeprecatedMethod */ + $collector = new TreeCollector( $environment, - $this->getPanel(), + $panel, $this->getViewSection()->getListingConfig()->getDefaultSortingFields(), $this->getTreeNodeStates() ); + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + $collection = $rootId ? $collector->getTreeCollectionRecursive($rootId, $level, $realProvider) : $collector->getChildrenOf( $realProvider, $level, - $environment->getInputProvider()->hasParameter('pid') ? $this->loadParentModel() : null + $inputProvider->hasParameter('pid') ? $this->loadParentModel() : null ); if ($rootId) { $treeData = $dataDriver->getEmptyCollection(); $model = $collection->get(0); + assert($model instanceof ModelInterface); if (!$model->getMeta(ModelInterface::HAS_CHILDREN)) { return $treeData; @@ -268,8 +310,12 @@ public function loadCollection($rootId = null, $level = 0, $providerName = null) protected function loadParentModel() { $environment = $this->getEnvironment(); + assert($environment instanceof EnvironmentInterface); - if (!($parentId = $environment->getInputProvider()->getParameter('pid'))) { + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + + if (!($parentId = $inputProvider->getParameter('pid'))) { throw new DcGeneralRuntimeException( 'TreeView needs a proper parent id defined, somehow none is defined?', 1 @@ -318,23 +364,36 @@ protected function calcLabelFields($providerName) */ protected function parseModel($model, $toggleID) { - $event = new FormatModelLabelEvent($this->environment, $model); - $this->environment->getEventDispatcher()->dispatch($event, DcGeneralEvents::FORMAT_MODEL_LABEL); + $environment = $this->environment; + assert($environment instanceof EnvironmentInterface); + + $event = new FormatModelLabelEvent($environment, $model); + + $environment = $this->getEnvironment(); + assert($environment instanceof EnvironmentInterface); + + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $dispatcher->dispatch($event, DcGeneralEvents::FORMAT_MODEL_LABEL); $model->setMeta($model::LABEL_VALUE, $event->getLabel()); $template = $this->getTemplate('dcbe_general_treeview_entry'); + $translator = $environment->getTranslator(); + assert($translator instanceof TranslatorInterface); + if ($model->getMeta($model::SHOW_CHILDREN)) { - $toggleTitle = $this->getEnvironment()->getTranslator()->translate('MSC.collapseNode'); + $toggleTitle = $translator->translate('MSC.collapseNode'); } else { - $toggleTitle = $this->getEnvironment()->getTranslator()->translate('MSC.expandNode'); + $toggleTitle = $translator->translate('MSC.expandNode'); } $toggleUrlEvent = new AddToUrlEvent( 'ptg=' . $model->getId() . '&provider=' . $model->getProviderName() ); - $this->getEnvironment()->getEventDispatcher()->dispatch($toggleUrlEvent, ContaoEvents::BACKEND_ADD_TO_URL); + $dispatcher->dispatch($toggleUrlEvent, ContaoEvents::BACKEND_ADD_TO_URL); $toggleData = [ 'url' => \html_entity_decode($toggleUrlEvent->getUrl()), @@ -377,9 +436,12 @@ protected function generateTreeView($collection, $treeClass) { $content = []; + $environment = $this->environment; + assert($environment instanceof EnvironmentInterface); + // Generate buttons - only if not in select mode! if (!$this->isSelectModeActive()) { - (new ButtonRenderer($this->environment))->renderButtonsForCollection($collection); + (new ButtonRenderer($environment))->renderButtonsForCollection($collection); } foreach ($collection as $model) { @@ -420,17 +482,30 @@ protected function generateTreeView($collection, $treeClass) */ public static function renderPasteRootButton(GetPasteRootButtonEvent $event) { - if (null !== $event->getHtml()) { - return $event->getHtml(); + if (null !== ($button = $event->getHtml())) { + return $button; } + $environment = $event->getEnvironment(); - $label = $environment->getTranslator()->translate( + assert($environment instanceof EnvironmentInterface); + + $translator = $environment->getTranslator(); + assert($translator instanceof TranslatorInterface); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $label = $translator->translate( 'pasteinto.0', - $environment->getDataDefinition()->getName() + $definition->getName() ); + + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + if ($event->isPasteDisabled()) { /** @var GenerateHtmlEvent $imageEvent */ - $imageEvent = $environment->getEventDispatcher()->dispatch( + $imageEvent = $dispatcher->dispatch( new GenerateHtmlEvent( 'pasteinto_.svg', $label, @@ -439,11 +514,11 @@ public static function renderPasteRootButton(GetPasteRootButtonEvent $event) ContaoEvents::IMAGE_GET_HTML ); - return $imageEvent->getHtml(); + return $imageEvent->getHtml() ?? ''; } /** @var GenerateHtmlEvent $imageEvent */ - $imageEvent = $environment->getEventDispatcher()->dispatch( + $imageEvent = $dispatcher->dispatch( new GenerateHtmlEvent( 'pasteinto.svg', $label, @@ -457,7 +532,7 @@ public static function renderPasteRootButton(GetPasteRootButtonEvent $event) $event->getHref(), StringUtil::specialchars($label), 'onclick="Backend.getScrollOffset()"', - $imageEvent->getHtml() + $imageEvent->getHtml() ?? '' ); } @@ -467,14 +542,20 @@ public static function renderPasteRootButton(GetPasteRootButtonEvent $event) * @param CollectionInterface $collection The collection of items. * * @return string + * + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ protected function viewTree($collection) { $definition = $this->getDataDefinition(); $listing = $this->getViewSection()->getListingConfig(); $basicDefinition = $definition->getBasicDefinition(); - $environment = $this->getEnvironment(); - $dispatcher = $environment->getEventDispatcher(); + + $environment = $this->getEnvironment(); + assert($environment instanceof EnvironmentInterface); + + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); // Init some Vars switch (6) { @@ -496,19 +577,26 @@ protected function viewTree($collection) if (null === $listing->getRootIcon()) { $labelIcon = 'pagemounts.svg'; } else { - $labelIcon = $listing->getRootIcon(); + $labelIcon = $listing->getRootIcon() ?? ''; } $filter = new Filter(); - $filter->andModelIsFromProvider($basicDefinition->getDataProvider()); + + $dataProvider = $basicDefinition->getDataProvider(); + assert(\is_string($dataProvider)); + + $filter->andModelIsFromProvider($dataProvider); if ($parentProviderName = $basicDefinition->getParentDataProvider()) { $filter->andParentIsFromProvider($parentProviderName); } else { $filter->andHasNoParent(); } + $clipboard = $environment->getClipboard(); + assert($clipboard instanceof ClipboardInterface); + // Root paste into. - if ($environment->getClipboard()->isNotEmpty($filter)) { + if ($clipboard->isNotEmpty($filter)) { /** @var AddToUrlEvent $urlEvent */ $urlEvent = $dispatcher->dispatch( new AddToUrlEvent( @@ -520,7 +608,7 @@ protected function viewTree($collection) ContaoEvents::BACKEND_ADD_TO_URL ); - $buttonEvent = new GetPasteRootButtonEvent($this->getEnvironment()); + $buttonEvent = new GetPasteRootButtonEvent($environment); $buttonEvent ->setHref($urlEvent->getUrl()) ->setPasteDisabled(false); @@ -551,7 +639,7 @@ protected function viewTree($collection) $this->formActionForSelect($template); // Add breadcrumb, if we have one. - if (null !== ($breadcrumb = $this->breadcrumb())) { + if ('' !== ($breadcrumb = $this->breadcrumb())) { $template->set('breadcrumb', $breadcrumb); } @@ -568,12 +656,20 @@ protected function viewTree($collection) protected function formActionForSelect(ContaoBackendViewTemplate $template) { $environment = $this->getEnvironment(); - if (!$template->get('select') || ('select' !== $environment->getInputProvider()->getParameter('act'))) { + assert($environment instanceof EnvironmentInterface); + + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + + if (!$template->get('select') || ('select' !== $inputProvider->getParameter('act'))) { return; } + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + $actionUrlEvent = new AddToUrlEvent('select=properties'); - $environment->getEventDispatcher()->dispatch($actionUrlEvent, ContaoEvents::BACKEND_ADD_TO_URL); + $dispatcher->dispatch($actionUrlEvent, ContaoEvents::BACKEND_ADD_TO_URL); $template->set('action', $actionUrlEvent->getUrl()); } @@ -581,17 +677,22 @@ protected function formActionForSelect(ContaoBackendViewTemplate $template) /** * {@inheritDoc} * + * @param ModelInterface $model + * * @deprecated Use ContaoCommunityAlliance\DcGeneral\EventListener\ModelRelationship\TreeEnforcingListener * - * @see \ContaoCommunityAlliance\DcGeneral\EventListener\ModelRelationship\TreeEnforcingListener + * @see TreeEnforcingListener * * @return void */ public function enforceModelRelationship($model) { + $environment = $this->getEnvironment(); + assert($environment instanceof EnvironmentInterface); + // Fallback implementation. $listener = new TreeEnforcingListener(); - $listener->process(new EnforceModelRelationshipEvent($this->getEnvironment(), $model)); + $listener->process(new EnforceModelRelationshipEvent($environment, $model)); } /** @@ -600,7 +701,12 @@ public function enforceModelRelationship($model) public function showAll(Action $action) { $environment = $this->getEnvironment(); - if ($environment->getDataDefinition()->getBasicDefinition()->isEditOnlyMode()) { + assert($environment instanceof EnvironmentInterface); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + if ($definition->getBasicDefinition()->isEditOnlyMode()) { return $this->edit($action); } @@ -609,8 +715,11 @@ public function showAll(Action $action) $collection = $this->loadCollection(); $content = []; - $viewEvent = new ViewEvent($this->environment, $action, DcGeneralViews::CLIPBOARD, []); - $environment->getEventDispatcher()->dispatch($viewEvent, DcGeneralEvents::VIEW); + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $viewEvent = new ViewEvent($environment, $action, DcGeneralViews::CLIPBOARD, []); + $dispatcher->dispatch($viewEvent, DcGeneralEvents::VIEW); // A list with ignored panels. $ignoredPanels = [ @@ -618,7 +727,7 @@ public function showAll(Action $action) SortElementInterface::class ]; - $content['language'] = $this->languageSwitcher($this->environment); + $content['language'] = $this->languageSwitcher($environment); $content['panel'] = $this->panel($ignoredPanels); $content['buttons'] = $this->generateHeaderButtons(); $content['clipboard'] = $viewEvent->getResponse(); @@ -628,7 +737,7 @@ public function showAll(Action $action) } /** - * Execute the multi language support. + * Execute the multi-language support. * * @param EnvironmentInterface $environment The environment. * @@ -645,10 +754,16 @@ private function languageSwitcher(EnvironmentInterface $environment) /** @var MultiLanguageDataProviderInterface $dataProvider */ + $controller = $environment->getController(); + assert($controller instanceof ControllerInterface); + + $translator = $environment->getTranslator(); + assert($translator instanceof TranslatorInterface); + return $template - ->set('languages', $environment->getController()->getSupportedLanguages(null)) + ->set('languages', $controller->getSupportedLanguages(null)) ->set('language', $dataProvider->getCurrentLanguage()) - ->set('submit', $this->environment->getTranslator()->translate('MSC.showSelected')) + ->set('submit', $translator->translate('MSC.showSelected')) ->set('REQUEST_TOKEN', $this->tokenManager->getToken($this->tokenName)) ->parse(); } @@ -666,7 +781,11 @@ private function languageSwitcher(EnvironmentInterface $environment) */ public function handleAjaxCall() { - $input = $this->getEnvironment()->getInputProvider(); + $environment = $this->getEnvironment(); + assert($environment instanceof EnvironmentInterface); + + $input = $environment->getInputProvider(); + assert($input instanceof InputProviderInterface); if ('DcGeneralLoadSubTree' !== $input->getValue('action')) { parent::handleAjaxCall(); @@ -701,6 +820,8 @@ public function ajaxTreeView($rootId, $providerName, $level) $collection = $this->loadCollection($rootId, $level, $providerName); $treeClass = ''; + // The switch is already prepared for more modes. + /** @psalm-suppress TypeDoesNotContainType */ switch (6) { case 5: $treeClass = 'tree'; @@ -717,17 +838,27 @@ public function ajaxTreeView($rootId, $providerName, $level) } /** - * Get the the container of selections. + * Get the container of selections. * * @return array */ - private function getSelectContainer() + private function getSelectContainer(): array { $environment = $this->getEnvironment(); + assert($environment instanceof EnvironmentInterface); + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $sessionName = $definition->getName() . '.' . $inputProvider->getParameter('mode'); + + $sessionStorage = $environment->getSessionStorage(); + assert($sessionStorage instanceof SessionStorageInterface); - $sessionName = $environment->getDataDefinition()->getName() . '.' . $inputProvider->getParameter('mode'); - if (!$environment->getSessionStorage()->has($sessionName)) { + if (!$sessionStorage->has($sessionName)) { return []; } @@ -736,7 +867,7 @@ private function getSelectContainer() return []; } - $session = $environment->getSessionStorage()->get($sessionName); + $session = $sessionStorage->get($sessionName); if (!\array_key_exists($selectAction, $session)) { return []; } diff --git a/src/Contao/View/Contao2BackendView/ViewHelpers.php b/src/Contao/View/Contao2BackendView/ViewHelpers.php index c92d088e..ac64737e 100644 --- a/src/Contao/View/Contao2BackendView/ViewHelpers.php +++ b/src/Contao/View/Contao2BackendView/ViewHelpers.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2021 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -16,7 +16,8 @@ * @author David Molineus * @author Stefan Heimes * @author Sven Baumann - * @copyright 2013-2021 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -28,18 +29,23 @@ use ContaoCommunityAlliance\DcGeneral\Contao\DataDefinition\Definition\Contao2BackendViewDefinitionInterface; use ContaoCommunityAlliance\DcGeneral\Data\ConfigInterface; use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\Properties\PropertyInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\GroupAndSortingDefinitionInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\GroupAndSortingInformationInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\ListingConfigInterface; use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; +use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; use ContaoCommunityAlliance\DcGeneral\Panel\PanelContainerInterface; use ContaoCommunityAlliance\DcGeneral\Panel\PanelInterface; use ContaoCommunityAlliance\DcGeneral\Panel\SortElementInterface; use ContaoCommunityAlliance\DcGeneral\View\Event\RenderReadablePropertyValueEvent; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; /** * Helper class that provides static methods used in views. + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class ViewHelpers { @@ -48,24 +54,29 @@ class ViewHelpers * * @param EnvironmentInterface $environment The environment. * - * @return GroupAndSortingDefinitionInterface + * @return GroupAndSortingDefinitionInterface|null */ public static function getCurrentSorting(EnvironmentInterface $environment) { /** @var BackendViewInterface $view */ $view = $environment->getView(); - foreach ($view->getPanel() as $panel) { + $panelInterface = $view->getPanel(); + assert($panelInterface instanceof PanelContainerInterface); + + foreach ($panelInterface as $panel) { /** @var PanelInterface $panel */ $sort = $panel->getElement('sort'); - if ($sort) { - /** @var SortElementInterface $sort */ + if ($sort instanceof SortElementInterface) { return $sort->getSelectedDefinition(); } } + $dataDefinition = $environment->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + /** @var Contao2BackendViewDefinitionInterface $viewSection */ - $viewSection = $environment->getDataDefinition()->getDefinition(Contao2BackendViewDefinitionInterface::NAME); + $viewSection = $dataDefinition->getDefinition(Contao2BackendViewDefinitionInterface::NAME); $definition = $viewSection->getListingConfig()->getGroupAndSortingDefinition(); if ($definition->hasDefault()) { return $definition->getDefault(); @@ -76,25 +87,31 @@ public static function getCurrentSorting(EnvironmentInterface $environment) /** * {@inheritDoc} + * + * @return string|null */ public static function getManualSortingProperty(EnvironmentInterface $environment) { /** @var BackendViewInterface $view */ $view = $environment->getView(); + $panelInterface = $view->getPanel(); + assert($panelInterface instanceof PanelContainerInterface); + $definition = null; - foreach ($view->getPanel() as $panel) { + foreach ($panelInterface as $panel) { /** @var PanelInterface $panel */ $sort = $panel->getElement('sort'); - if ($sort) { - /** @var SortElementInterface $sort */ + if ($sort instanceof SortElementInterface) { $definition = $sort->getSelectedDefinition(); } } if (null === $definition) { + $dataDefinition = $environment->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + /** @var Contao2BackendViewDefinitionInterface $viewDefinition */ - $dataDefinition = $environment->getDataDefinition(); $viewDefinition = $dataDefinition->getDefinition(Contao2BackendViewDefinitionInterface::NAME); $groupAndSorting = $viewDefinition->getListingConfig()->getGroupAndSortingDefinition(); @@ -206,7 +223,10 @@ public static function getReadableFieldValue( $model->getProperty($property->getName()) ); - $environment->getEventDispatcher()->dispatch($event, $event::NAME); + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $dispatcher->dispatch($event, $event::NAME); if (null !== $event->getRendered()) { return $event->getRendered(); @@ -225,9 +245,10 @@ public static function getReadableFieldValue( public static function redirectHome(EnvironmentInterface $environment) { $input = $environment->getInputProvider(); + assert($input instanceof InputProviderInterface); - if ($input->hasParameter('table') && ($hasParentId = $input->hasParameter('pid'))) { - if ($hasParentId) { + if ($input->hasParameter('table')) { + if ($input->hasParameter('pid')) { $event = new RedirectEvent( \sprintf( 'contao?do=%s&table=%s&pid=%s', @@ -254,6 +275,9 @@ public static function redirectHome(EnvironmentInterface $environment) ); } - $environment->getEventDispatcher()->dispatch($event, ContaoEvents::CONTROLLER_REDIRECT); + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $dispatcher->dispatch($event, ContaoEvents::CONTROLLER_REDIRECT); } } diff --git a/src/Contao/View/Contao2BackendView/Widget/AbstractWidget.php b/src/Contao/View/Contao2BackendView/Widget/AbstractWidget.php index 4e227d98..bc083007 100644 --- a/src/Contao/View/Contao2BackendView/Widget/AbstractWidget.php +++ b/src/Contao/View/Contao2BackendView/Widget/AbstractWidget.php @@ -30,6 +30,10 @@ * Abstract widget class as base for dc general backend widgets. * * This widget is only prepared to run in DcCompat mode! + * + * @psalm-suppress PropertyNotSetInConstructor + * + * @property ?DcCompat $objDca */ abstract class AbstractWidget extends Widget { @@ -50,9 +54,9 @@ abstract class AbstractWidget extends Widget /** * The data Container. * - * @var DcCompat + * @var DcCompat|null */ - protected $dataContainer; + protected $dataContainer = null; /** * Create a new instance. @@ -64,7 +68,9 @@ public function __construct($attributes = null, DcCompat $dataContainer = null) { parent::__construct($attributes); - $this->dataContainer = $dataContainer ?: $this->objDca; + $dataContainer = $dataContainer ?: $this->objDca; + assert(null === $dataContainer || $dataContainer instanceof DcCompat); + $this->dataContainer = $dataContainer; } /** @@ -74,6 +80,7 @@ public function __construct($attributes = null, DcCompat $dataContainer = null) */ public function getEnvironment() { + assert($this->dataContainer instanceof DcCompat); return $this->dataContainer->getEnvironment(); } @@ -84,6 +91,7 @@ public function getEnvironment() */ public function getModel() { + assert($this->dataContainer instanceof DcCompat); return $this->dataContainer->getModel(); } } diff --git a/src/Contao/View/Contao2BackendView/Widget/FileTree.php b/src/Contao/View/Contao2BackendView/Widget/FileTree.php index 6b4b82ee..6cc9b552 100644 --- a/src/Contao/View/Contao2BackendView/Widget/FileTree.php +++ b/src/Contao/View/Contao2BackendView/Widget/FileTree.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2022 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -16,7 +16,7 @@ * @author Stefan Heimes * @author Ingolf Steinhardt * @author Sven Baumann - * @copyright 2013-2022 Contao Community Alliance. + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -25,9 +25,12 @@ use Contao\Config; use Contao\CoreBundle\Exception\ResponseException; +use Contao\CoreBundle\Image\ImageFactoryInterface; +use Contao\CoreBundle\Picker\PickerBuilderInterface; use Contao\DataContainer; use Contao\Environment; use Contao\File; +use Contao\FileSelector; use Contao\FilesModel; use Contao\Image; use Contao\Image\ResizeConfiguration; @@ -36,21 +39,33 @@ use Contao\System; use ContaoCommunityAlliance\DcGeneral\Contao\Compatibility\DcCompat; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\ContaoBackendViewTemplate; +use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; +use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; +use ContaoCommunityAlliance\Translator\TranslatorInterface; use Symfony\Component\HttpFoundation\Response; /** * File tree widget being compatible with the dc general. * - * @property string strField The field name. - * @property bool mandatory If true the field is required. - * @property bool multiple If true multiple values are allowed. - * @property bool isGallery If true the a image gallery is rendered. - * @property bool isDownloads If true only allowed download files are listed. + * @property string $strField The field name. + * @property bool $mandatory If true the field is required. + * @property bool $multiple If true multiple values are allowed. + * @property bool $isGallery If true the image gallery is rendered. + * @property bool $isDownloads If true only allowed download files are listed. + * @property string $fieldType Either 'radio' or 'checkbox'. + * @property bool|null $files Flag if files shall be shown. + * @property bool|null $filesOnly Flag if only files shall be selectable. + * @property string|null $path The root path. + * @property string|null $extensions The list of valid file extensions. * * @see https://github.com/contao/core/blob/master/system/modules/core/widgets/FileTree.php * * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * @psalm-suppress PropertyNotSetInConstructor + * + * @deprecated Deprecated since Contao 4.13 as the used FilePicker has been deprecated. Use the picker instead. */ class FileTree extends AbstractWidget { @@ -71,16 +86,16 @@ class FileTree extends AbstractWidget /** * The order field attribute name. * - * @var string. + * @var string */ - protected $orderField; + protected $orderField = ''; /** * The order field value. * - * @var array. + * @var null|list */ - protected $orderFieldValue; + protected $orderFieldValue = null; /** * The default width of the thumbnail. @@ -171,7 +186,9 @@ public function __set($strKey, $varValue) * * @param string $strKey The property name. * - * @return string The property value + * @return bool|int|null|object|string The property value + * + * @psalm-suppress ImplementedReturnTypeMismatch - Widget incorrectly annotates that it only returns string. */ public function __get($strKey) { @@ -199,7 +216,7 @@ public function __get($strKey) * * @param string $strKey The property name. * - * @return boolean True if the property exists + * @return bool True if the property exists */ public function __isset($strKey) { @@ -217,12 +234,12 @@ public function __isset($strKey) return isset($this->placeholderImage); default: - return parent::__get($strKey); + return (bool) parent::__get($strKey); } } /** - * Setup the file tree widget. + * Set up the file tree widget. * * @return void * @@ -234,75 +251,93 @@ private function setUp() // Load the fonts for the drag hint (see #4838) $GLOBALS['TL_CONFIG']['loadGoogleFonts'] = true; - $this->projectDir = System::getContainer()->getParameter('kernel.project_dir'); + $projectDir = System::getContainer()->getParameter('kernel.project_dir'); + assert(\is_string($projectDir)); + $this->projectDir = $projectDir; - if (!$this->dataContainer || !$this->orderField) { + if (!$this->orderField) { return; } - $value = $this->dataContainer->getModel()->getProperty($this->orderField); + assert($this->dataContainer instanceof DcCompat); + $model = $this->dataContainer->getModel(); + assert($model instanceof ModelInterface); + + $value = $model->getProperty($this->orderField); // support serialized values. if (!\is_array($value)) { $value = StringUtil::deserialize($value, true); + assert(\is_array($value)); } + $value = \array_values($value); + /** @var list $value */ - $this->orderId = $this->orderField . \str_replace($this->strField, '', $this->strId); - $this->orderFieldValue = (!empty($value) && \is_array($value)) ? \array_filter($value) : []; + $this->orderId = $this->orderField . \str_replace($this->strField, '', (string) $this->strId); + $this->orderFieldValue = \array_values(\array_filter($value)); } /** * Process the validation. * - * @param mixed $inputValue The input value. + * @param mixed $varInput The input value. * * @return array|string */ - protected function validator($inputValue) + protected function validator($varInput) { - if ('' === $inputValue) { + if ('' === $varInput) { if ($this->mandatory) { + $translator = $this->getEnvironment()->getTranslator(); + assert($translator instanceof TranslatorInterface); + $this->addError( - $this->getEnvironment()->getTranslator()->translate('mandatory', 'ERR', [$this->strLabel]) + $translator->translate('mandatory', 'ERR', [$this->strLabel]) ); } return ''; } - $inputValue = \array_map('\Contao\StringUtil::uuidToBin', \array_filter(\explode(',', $inputValue))); + $varInput = \array_map('\Contao\StringUtil::uuidToBin', \array_filter(\explode(',', $varInput))); - return $this->multiple ? $inputValue : $inputValue[0]; + return $this->multiple ? $varInput : $varInput[0]; } /** * Render the file list. * * @param array $icons The generated icons. - * @param Collection|null $collection The files collection. + * @param Collection|null $collection The file's collection. * - * @param bool $followSubDirs If true subfolders get rendered. + * @param bool $followSubDirs If true sub-folders get rendered. * * @return void */ - private function renderList(array &$icons, Collection $collection = null, $followSubDirs = false) + private function renderList(array &$icons, Collection $collection = null, bool $followSubDirs = false) { if (!$collection) { return; } foreach ($collection->getModels() as $model) { + assert($model instanceof FilesModel); + $uuid = $model->uuid; + assert(is_string($uuid)); // File system and database seem not in sync if (!\file_exists($this->projectDir . '/' . $model->path)) { continue; } if (('folder' === $model->type) && $followSubDirs) { - $this->renderList($icons, FilesModel::findByPid($model->uuid)); + $files = FilesModel::findByPid($uuid); + assert($files instanceof Collection); + $this->renderList($icons, $files); continue; } + if (false !== ($icon = $this->renderIcon($model, $this->isGallery, $this->isDownloads))) { - $icons[\md5($model->uuid)] = ['uuid' => $model->uuid, 'image' => $icon]; + $icons[\md5($uuid)] = ['uuid' => $uuid, 'image' => $icon]; } } } @@ -397,14 +432,16 @@ private function generateGalleryImage(File $file, $info) ) ) { // Inline the image if no preview image will be generated (see #636). - if ( - $file->height !== null && $file->height <= $this->thumbnailHeight - && $file->width !== null && $file->width <= $this->thumbnailWidth - ) { + if ($file->height <= $this->thumbnailHeight && $file->width <= $this->thumbnailWidth) { $image = $file->dataUri; } else { $projectDir = System::getContainer()->getParameter('kernel.project_dir'); - $image = System::getContainer()->get('contao.image.image_factory')->create( + assert(\is_string($projectDir)); + + $imageFactory = System::getContainer()->get('contao.image.image_factory'); + assert($imageFactory instanceof ImageFactoryInterface); + + $image = $imageFactory->create( $projectDir . '/' . $file->path, [$this->thumbnailWidth, $this->thumbnailHeight, ResizeConfiguration::MODE_BOX] )->getUrl($projectDir); @@ -468,15 +505,18 @@ private function generateLink() $extras['filesOnly'] = (bool) $this->filesOnly; } - if ($this->path) { - $extras['path'] = (string) $this->path; + if (\is_string($this->path)) { + $extras['path'] = $this->path; } - if ($this->extensions) { - $extras['extensions'] = (string) $this->extensions; + if (\is_string($this->extensions)) { + $extras['extensions'] = $this->extensions; } - return System::getContainer()->get('contao.picker.builder')->getUrl('file', $extras); + $pickerBuilder = System::getContainer()->get('contao.picker.builder'); + assert($pickerBuilder instanceof PickerBuilderInterface); + + return $pickerBuilder->getUrl('file', $extras); } /** @@ -489,8 +529,12 @@ public function generate() if (!empty($this->varValue)) { $files = FilesModel::findMultipleByUuids((array) $this->varValue); - $this->renderList($icons, $files, $this->isGallery || $this->isDownloads); - //dump($icons); + + if (null !== $files) { + assert($files instanceof Collection); + $this->renderList($icons, $files, $this->isGallery || $this->isDownloads); + } + $icons = $this->applySorting($icons); // Files can be null. @@ -501,12 +545,15 @@ public function generate() } } + $translator = $this->getEnvironment()->getTranslator(); + assert($translator instanceof TranslatorInterface); + $content = (new ContaoBackendViewTemplate($this->subTemplate)) - ->setTranslator($this->getEnvironment()->getTranslator()) + ->setTranslator($translator) ->set('name', $this->strName) ->set('id', $this->strId) ->set('value', \implode(',', $values)) - ->set('hasOrder', $this->orderField != '' && \is_array($this->orderFieldValue)) + ->set('hasOrder', $this->orderField !== '' && \is_array($this->orderFieldValue)) ->set('icons', $icons) ->set('isGallery', $this->isGallery) ->set('orderId', $this->orderId) @@ -538,14 +585,20 @@ public function updateAjax($ajaxAction, DataContainer $dataContainer) return ''; } + assert($dataContainer instanceof DcCompat); $this->dataContainer = $dataContainer; $this->setUp(); - $environment = $this->dataContainer->getEnvironment(); + $environment = $this->dataContainer->getEnvironment(); + $dataDefinition = $environment->getDataDefinition(); - $inputProvider = $environment->getInputProvider(); - $propertyName = $inputProvider->getValue('name'); - $information = (array) $GLOBALS['TL_DCA'][$dataContainer->getName()]['fields'][$propertyName]; + assert($dataDefinition instanceof ContainerInterface); + + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + + $propertyName = $inputProvider->getValue('name'); + $information = (array) $GLOBALS['TL_DCA'][$dataContainer->getName()]['fields'][$propertyName]; // Merge with the information from the data container. $information['eval'] = \array_merge( @@ -555,10 +608,11 @@ public function updateAjax($ajaxAction, DataContainer $dataContainer) $combat = new DcCompat($environment, null, $propertyName); - /** @var \FileSelector $widgetClass */ + /** @var class-string $widgetClass */ + /** @psalm-suppress DeprecatedClass - we know we are deprecated ourselves. :D */ $widgetClass = $GLOBALS['BE_FFL']['fileSelector']; - /** @var \FileSelector $widget */ + /** @psalm-suppress UnsafeInstantiation - no better way to instantiate :( */ $widget = new $widgetClass( $widgetClass::getAttributesFromDca( $information, diff --git a/src/Contao/View/Contao2BackendView/Widget/FileTreeOrder.php b/src/Contao/View/Contao2BackendView/Widget/FileTreeOrder.php index 92c8567e..c2559a48 100644 --- a/src/Contao/View/Contao2BackendView/Widget/FileTreeOrder.php +++ b/src/Contao/View/Contao2BackendView/Widget/FileTreeOrder.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -15,7 +15,8 @@ * @author Sven Baumann * @author Christian Schiffler * @author Stefan Heimes - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -26,6 +27,8 @@ * This widget is a supporting widget to store the file tree orderings. * * The ContaoWidgetManager does not allow input values without a widget. This is used as helper widget instead. + * + * @psalm-suppress PropertyNotSetInConstructor */ class FileTreeOrder extends AbstractWidget { @@ -39,11 +42,9 @@ class FileTreeOrder extends AbstractWidget /** * {@inheritdoc} */ - protected function validator($inputValue) + protected function validator($varInput) { - $inputValue = \array_map('\Contao\StringUtil::uuidToBin', \array_filter(\explode(',', $inputValue))); - - return $inputValue; + return \array_map('\Contao\StringUtil::uuidToBin', \array_filter(\explode(',', $varInput))); } /** diff --git a/src/Contao/View/Contao2BackendView/Widget/PageTree.php b/src/Contao/View/Contao2BackendView/Widget/PageTree.php index a267c700..b57a30d0 100644 --- a/src/Contao/View/Contao2BackendView/Widget/PageTree.php +++ b/src/Contao/View/Contao2BackendView/Widget/PageTree.php @@ -20,14 +20,20 @@ namespace ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Widget; +use Contao\CoreBundle\Picker\PickerBuilderInterface; use Contao\System; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\TreePicker; use ContaoCommunityAlliance\DcGeneral\Data\CollectionInterface; +use ContaoCommunityAlliance\DcGeneral\Data\DataProviderInterface; +use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; +use ContaoCommunityAlliance\Translator\TranslatorInterface; /** * Page tree widget being compatible with the dc general. * * @see https://github.com/contao/core/blob/master/system/modules/core/widgets/PageTree.php + * + * @psalm-suppress PropertyNotSetInConstructor */ class PageTree extends TreePicker { @@ -41,15 +47,17 @@ class PageTree extends TreePicker /** * Process the validation. * - * @param mixed $inputValue The input value. + * @param mixed $varInput The input value. + * + * @return null|string|list * - * @return array|string */ - protected function validator($inputValue) + protected function validator($varInput) { $translator = $this->getEnvironment()->getTranslator(); + assert($translator instanceof TranslatorInterface); - $widgetValue = $this->widgetToValue($inputValue); + $widgetValue = $this->widgetToValue($varInput); if ((null === $widgetValue) && $this->mandatory) { $this->addError($translator->translate('mandatory', 'ERR', [$this->strLabel])); } @@ -70,9 +78,14 @@ public function loadCollection($rootId = null, $level = 0, $providerName = null) { $collection = $this->getTreeCollectionRecursive($rootId, $level, $providerName); - $treeData = $this->getEnvironment()->getDataProvider($providerName)->getEmptyCollection(); + $dataProvider = $this->getEnvironment()->getDataProvider($providerName); + assert($dataProvider instanceof DataProviderInterface); + + $treeData = $dataProvider->getEmptyCollection(); if ($rootId) { $objModel = $collection->get(0); + assert($objModel instanceof ModelInterface); + foreach ($objModel->getMeta($objModel::CHILD_COLLECTIONS) as $childCollection) { foreach ($childCollection as $subModel) { $treeData->push($subModel); @@ -103,8 +116,9 @@ protected function generatePickerUrl() 'fieldType' => $this->fieldType ]; - return System::getContainer() - ->get('contao.picker.builder') - ->getUrl('page', $extra); + $pickerBuilder = System::getContainer()->get('contao.picker.builder'); + assert($pickerBuilder instanceof PickerBuilderInterface); + + return $pickerBuilder->getUrl('page', $extra); } } diff --git a/src/Contao/View/Contao2BackendView/Widget/PageTreeOrder.php b/src/Contao/View/Contao2BackendView/Widget/PageTreeOrder.php index dcfea41e..21e13c6c 100644 --- a/src/Contao/View/Contao2BackendView/Widget/PageTreeOrder.php +++ b/src/Contao/View/Contao2BackendView/Widget/PageTreeOrder.php @@ -23,6 +23,8 @@ * This widget is a supporting widget to store the page tree orderings. * * The ContaoWidgetManager does not allow input values without a widget. This is used as helper widget instead. + * + * @psalm-suppress PropertyNotSetInConstructor */ class PageTreeOrder extends AbstractWidget { diff --git a/src/Contao/View/Contao2BackendView/Widget/TreePickerOrder.php b/src/Contao/View/Contao2BackendView/Widget/TreePickerOrder.php index ac9e77d1..9dd81a68 100644 --- a/src/Contao/View/Contao2BackendView/Widget/TreePickerOrder.php +++ b/src/Contao/View/Contao2BackendView/Widget/TreePickerOrder.php @@ -23,6 +23,8 @@ * This widget is a supporting widget to store the page tree orderings. * * The ContaoWidgetManager does not allow input values without a widget. This is used as helper widget instead. + * + * @psalm-suppress PropertyNotSetInConstructor */ class TreePickerOrder extends AbstractWidget { @@ -45,9 +47,9 @@ public function generate() /** * {@inheritdoc} */ - protected function validator($inputValue) + protected function validator($varInput) { - return \array_filter(\explode(',', $inputValue)); + return \array_filter(\explode(',', $varInput)); } /** diff --git a/src/ContaoManager/Plugin.php b/src/ContaoManager/Plugin.php index a71c8568..e9f77c11 100644 --- a/src/ContaoManager/Plugin.php +++ b/src/ContaoManager/Plugin.php @@ -29,6 +29,7 @@ use ContaoCommunityAlliance\Contao\Bindings\CcaEventsContaoBindingsBundle; use ContaoCommunityAlliance\DcGeneral\CcaDcGeneralBundle; use ContaoCommunityAlliance\UrlBuilder\CcaUrlBuilderBundle; +use Symfony\Component\Config\Loader\LoaderInterface; use Symfony\Component\Config\Loader\LoaderResolverInterface; use Symfony\Component\HttpKernel\KernelInterface; @@ -59,8 +60,8 @@ public function getBundles(ParserInterface $parser) */ public function getRouteCollection(LoaderResolverInterface $resolver, KernelInterface $kernel) { - return $resolver - ->resolve(__DIR__ . '/../Resources/config/routing.yml') - ->load(__DIR__ . '/../Resources/config/routing.yml'); + $loader = $resolver->resolve(__DIR__ . '/../Resources/config/routing.yml'); + assert($loader instanceof LoaderInterface); + return $loader->load(__DIR__ . '/../Resources/config/routing.yml'); } } diff --git a/src/Controller/Ajax.php b/src/Controller/Ajax.php index e32f2cbb..d11c442d 100644 --- a/src/Controller/Ajax.php +++ b/src/Controller/Ajax.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -27,25 +28,32 @@ use Contao\Database; use Contao\System; use ContaoCommunityAlliance\DcGeneral\Contao\Compatibility\DcCompat; +use ContaoCommunityAlliance\DcGeneral\Data\DataProviderInterface; use ContaoCommunityAlliance\DcGeneral\Data\ModelId; use ContaoCommunityAlliance\DcGeneral\DataContainerInterface; +use ContaoCommunityAlliance\DcGeneral\DC\General; use ContaoCommunityAlliance\DcGeneral\EnvironmentAwareInterface; +use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBagInterface; +use Symfony\Component\HttpFoundation\Session\SessionInterface; /** * Class Ajax - General purpose Ajax handler for "executePostActions" as we can not use the default Contao * handling. * * See Contao core issue #5957. https://github.com/contao/core/pull/5957 + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ abstract class Ajax implements EnvironmentAwareInterface { /** * The data container calling us. * - * @var DataContainerInterface + * @var null|DataContainerInterface */ - protected $objDc; + protected $objDc = null; /** * Create a new instance. @@ -58,10 +66,13 @@ public function __construct() /** * Get the data container. * - * @return DataContainerInterface. + * @return General */ protected function getDataContainer() { + if (!$this->objDc instanceof General) { + throw new \LogicException('Must be set first via ' . self::class . '::executePostActions().'); + } return $this->objDc; } @@ -83,7 +94,10 @@ public function getEnvironment() */ protected function getGet($key, $decodeEntities = false) { - return $this->getEnvironment()->getInputProvider()->getParameter($key, $decodeEntities); + $inputProvider = $this->getEnvironment()->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + + return $inputProvider->getParameter($key, $decodeEntities); } /** @@ -92,11 +106,18 @@ protected function getGet($key, $decodeEntities = false) * @param string $key The key to retrieve. * @param bool $decodeEntities Decode the entities. * - * @return mixed + * @return string|null */ protected function getPost($key, $decodeEntities = false) { - return $this->getEnvironment()->getInputProvider()->getValue($key, $decodeEntities); + $inputProvider = $this->getEnvironment()->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + + $value = $inputProvider->getValue($key, $decodeEntities); + if ($value === null) { + return null; + } + return (string) $value; } /** @@ -108,7 +129,7 @@ protected function getPost($key, $decodeEntities = false) */ protected function getAjaxId() { - return preg_replace('/.*_([0-9a-zA-Z]+)$/', '$1', $this->getPost('id')); + return preg_replace('/.*_([0-9a-zA-Z]+)$/', '$1', (string) $this->getPost('id')); } /** @@ -116,13 +137,16 @@ protected function getAjaxId() * * This method exits the script! * - * @return void + * @return never * * @throws ResponseException Throws a response exception. + * + * @deprecated */ protected function loadStructure() { // Method ajaxTreeView is in TreeView.php - watch out! + /** @psalm-suppress DeprecatedMethod */ $response = new Response( $this->getDataContainer()->ajaxTreeView($this->getAjaxId(), (int) $this->getPost('level')) ); @@ -135,7 +159,7 @@ protected function loadStructure() * * This method exits the script! * - * @return void + * @return never * * @throws ResponseException Throws a response exception. */ @@ -206,7 +230,10 @@ public function executePostActions(DataContainerInterface $container) $this->objDc = $container; - $action = $this->getEnvironment()->getInputProvider()->getValue('action'); + $inputProvider = $this->getEnvironment()->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + + $action = $inputProvider->getValue('action'); if ( \in_array( @@ -258,16 +285,18 @@ public function executePostActions(DataContainerInterface $container) private function getActiveModel() { $input = $this->getEnvironment()->getInputProvider(); + assert($input instanceof InputProviderInterface); + if (false === $input->hasParameter('id')) { return null; } - $modelId = ModelId::fromSerialized($input->getParameter('id')); - $dataProvider = $this->getEnvironment()->getDataProvider($modelId->getDataProviderName()); + $modelId = ModelId::fromSerialized($input->getParameter('id')); - $model = $dataProvider->fetch($dataProvider->getEmptyConfig()->setId($modelId->getId())); + $dataProvider = $this->getEnvironment()->getDataProvider($modelId->getDataProviderName()); + assert($dataProvider instanceof DataProviderInterface); - return $model; + return $dataProvider->fetch($dataProvider->getEmptyConfig()->setId($modelId->getId())); } /** @@ -275,7 +304,7 @@ private function getActiveModel() * * Will get called from subclasses to have a central endpoint to exit the script. * - * @return void + * @return never * * @deprecated Deperecated since 2.1 and where remove in 3.0. Use own response exit. * @@ -287,8 +316,13 @@ protected function exitScript() @\trigger_error('Use own response exit!', E_USER_DEPRECATED); // @codingStandardsIgnoreEnd - $session = System::getContainer()->get('session'); - $sessionBag = $session->getBag('contao_backend')->all(); + $session = System::getContainer()->get('session'); + assert($session instanceof SessionInterface); + + $sessionBag = $session->getBag('contao_backend'); + assert($sessionBag instanceof AttributeBagInterface); + + $sessionBag = $sessionBag->all(); $user = BackendUser::getInstance(); diff --git a/src/Controller/Ajax3X.php b/src/Controller/Ajax3X.php index af0a2182..5ddf5824 100644 --- a/src/Controller/Ajax3X.php +++ b/src/Controller/Ajax3X.php @@ -27,17 +27,21 @@ use Contao\CoreBundle\Exception\ResponseException; use Contao\Dbafs; +use Contao\FileSelector; use Contao\PageSelector; use Contao\StringUtil; use Contao\Widget; use ContaoCommunityAlliance\Contao\Bindings\ContaoEvents; use ContaoCommunityAlliance\Contao\Bindings\Events\System\LogEvent; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\ContaoWidgetManager; +use ContaoCommunityAlliance\DcGeneral\Data\DataProviderInterface; use ContaoCommunityAlliance\DcGeneral\Data\ModelId; use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; use ContaoCommunityAlliance\DcGeneral\Data\PropertyValueBag; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; use ContaoCommunityAlliance\DcGeneral\SessionStorageInterface; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\HttpFoundation\Response; /** @@ -51,23 +55,29 @@ class Ajax3X extends Ajax /** * Get the widget instance. * - * @param string $fieldName The property name. - * @param string $serializedId The serialized id of the model. - * @param string $propertyValue The property value. + * @param string $fieldName The property name. + * @param string|null $serializedId The serialized id of the model. + * @param string $propertyValue The property value. * - * @return Widget + * @return Widget|null */ protected function getWidget($fieldName, $serializedId, $propertyValue) { - $environment = $this->getEnvironment(); - $property = $environment->getDataDefinition()->getPropertiesDefinition()->getProperty($fieldName); + $environment = $this->getEnvironment(); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $property = $definition->getPropertiesDefinition()->getProperty($fieldName); $propertyValues = new PropertyValueBag(); if (null !== $serializedId) { $model = $this->getModelFromSerializedId($serializedId); } else { $dataProvider = $environment->getDataProvider(); - $model = $dataProvider->getEmptyModel(); + assert($dataProvider instanceof DataProviderInterface); + + $model = $dataProvider->getEmptyModel(); } $widgetManager = new ContaoWidgetManager($environment, $model); @@ -77,10 +87,10 @@ protected function getWidget($fieldName, $serializedId, $propertyValue) $propertyValue = $this->getTreeValue($treeType, $propertyValue); if (('file' === $treeType) || ('page' === $treeType)) { $extra = $property->getExtra(); - if (\is_array($propertyValue) && !isset($extra['multiple'])) { + if (!isset($extra['multiple'])) { $propertyValue = $propertyValue[0]; } else { - $propertyValue = \implode(',', (array) $propertyValue); + $propertyValue = \implode(',', $propertyValue); } } @@ -102,17 +112,20 @@ protected function getWidget($fieldName, $serializedId, $propertyValue) protected function loadPagetree() { $environment = $this->getEnvironment(); - $input = $environment->getInputProvider(); - $session = $environment->getSessionStorage(); + $input = $environment->getInputProvider(); assert($input instanceof InputProviderInterface); + $session = $environment->getSessionStorage(); assert($session instanceof SessionStorageInterface); - $field = $input->getValue('field'); - $name = $input->getValue('name'); - $level = (int) $input->getValue('level'); - $rootId = $input->getValue('id'); + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $field = $input->getValue('field'); + $name = $input->getValue('name'); + $level = (int) $input->getValue('level'); + $rootId = (string) $input->getValue('id'); $ajaxId = \preg_replace('/.*_([0-9a-zA-Z]+)$/', '$1', $rootId); $ajaxKey = \str_replace('_' . $ajaxId, '', $rootId); @@ -126,14 +139,20 @@ protected function loadPagetree() $nodes[$ajaxId] = (int) $input->getValue('state'); $session->set($ajaxKey, $nodes); - $arrData['strTable'] = $environment->getDataDefinition()->getName(); - $arrData['id'] = $ajaxName ?: $rootId; - $arrData['name'] = $name; + $arrData = [ + 'strTable' => $definition->getName(), + 'id' => $ajaxName ?: $rootId, + 'name' => $name, + ]; - /** @var PageSelector $widget */ + /** + * @psalm-suppress DeprecatedClass + * @var PageSelector $widget + */ $widget = new $GLOBALS['BE_FFL']['pageSelector']($arrData, $this->getDataContainer()); $widget->value = $this->getTreeValue('page', $input->getValue('value')); + /** @psalm-suppress InvalidArgument - rather pass it "as is", we do not trust Contao annotations. */ $response = new Response($widget->generateAjax($ajaxId, $field, $level)); throw new ResponseException($response); @@ -150,20 +169,30 @@ protected function loadPagetree() protected function loadFiletree() { $environment = $this->getEnvironment(); - $input = $environment->getInputProvider(); - $folder = $input->getValue('folder'); - $field = $input->getValue('field'); - $level = (int) $input->getValue('level'); + $input = $environment->getInputProvider(); + assert($input instanceof InputProviderInterface); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $folder = $input->getValue('folder'); + $field = $input->getValue('field'); + $level = (int) $input->getValue('level'); + + $arrData = []; $arrData['strTable'] = $input->getParameter('table'); $arrData['id'] = $field; $arrData['name'] = $field; $arrData = \array_merge( - $environment->getDataDefinition()->getPropertiesDefinition()->getProperty($field)->getExtra(), + $definition->getPropertiesDefinition()->getProperty($field)->getExtra(), $arrData ); - /** @var \FileSelector $widget */ + /** + * @psalm-suppress DeprecatedClass + * @var FileSelector $widget + */ $widget = new $GLOBALS['BE_FFL']['fileSelector']($arrData, $this->getDataContainer()); $widget->value = $this->getTreeValue($field, $input->getValue('value')); @@ -185,23 +214,26 @@ protected function loadFiletree() * @param string $type Either "page" or "file". * @param string $value The value as comma separated list. * - * @return string The value array. + * @return list The value array. */ protected function getTreeValue($type, $value) { // Convert the selected values. - if ('' !== $value) { - $value = StringUtil::trimsplit("\t", $value); - - // Automatically add resources to the DBAFS. - if ('file' === $type) { - foreach ($value as $k => $v) { - $value[$k] = StringUtil::binToUuid(Dbafs::addResource(\urldecode($v))->uuid); - } + if ('' === $value) { + return []; + } + $value = StringUtil::trimsplit("\t", $value); + + // Automatically add resources to the DBAFS. + if ('file' === $type) { + foreach ($value as $k => $v) { + $uuid = Dbafs::addResource(\urldecode($v))->uuid; + assert(\is_string($uuid)); + $value[$k] = StringUtil::binToUuid($uuid); } } - return $value; + return array_values($value); } /** @@ -219,16 +251,25 @@ protected function getModelFromSerializedId($serializedId) { $modelId = ModelId::fromSerialized($serializedId); $dataProvider = $this->getEnvironment()->getDataProvider($modelId->getDataProviderName()); - $model = $dataProvider->fetch($dataProvider->getEmptyConfig()->setId($modelId->getId())); + assert($dataProvider instanceof DataProviderInterface); + + $model = $dataProvider->fetch($dataProvider->getEmptyConfig()->setId($modelId->getId())); if (null === $model) { + $definition = $this->getEnvironment()->getDataDefinition(); + assert($definition instanceof ContainerInterface); + $event = new LogEvent( 'A record with the ID "' . $serializedId . '" does not exist in "' . - $this->getEnvironment()->getDataDefinition()->getName() . '"', + $definition->getName() . '"', 'Ajax executePostActions()', 'ERROR' ); - $this->getEnvironment()->getEventDispatcher()->dispatch($event, ContaoEvents::SYSTEM_LOG); + + $dispatcher = $this->getEnvironment()->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $dispatcher->dispatch($event, ContaoEvents::SYSTEM_LOG); throw new ResponseException(new Response(Response::$statusTexts[400], 400)); } @@ -239,23 +280,33 @@ protected function getModelFromSerializedId($serializedId) /** * Reload the file tree. * - * @return void + * @return never * * @throws ResponseException Throws a response exception. */ protected function reloadTree() { - $input = $this->getEnvironment()->getInputProvider(); + $input = $this->getEnvironment()->getInputProvider(); + assert($input instanceof InputProviderInterface); + $serializedId = ($input->hasParameter('id') && $input->getParameter('id')) ? $input->getParameter('id') : null; - $value = $input->hasValue('value') ? $input->getValue('value', true) : null; + $value = $input->hasValue('value') ? $input->getValue('value', true) : ''; + + $fieldName = $this->getFieldName(); + assert(\is_string($fieldName)); + + $widget = $this->getWidget($fieldName, $serializedId, $value); + assert($widget instanceof Widget); - $this->generateWidget($this->getWidget($this->getFieldName(), $serializedId, $value)); + $this->generateWidget($widget); throw new ResponseException(new Response('')); } /** * {@inheritDoc} + * + * @return never */ protected function reloadPagetree() { @@ -264,6 +315,8 @@ protected function reloadPagetree() /** * {@inheritDoc} + * + * @return never */ protected function reloadFiletree() { @@ -274,13 +327,20 @@ protected function reloadFiletree() * {@inheritDoc} * * @throws ResponseException Throws a response exception. + * + * @return never */ protected function setLegendState() { $environment = $this->getEnvironment(); - $input = $environment->getInputProvider(); - $session = $environment->getSessionStorage(); - $states = $session->get('LEGENDS'); + + $input = $environment->getInputProvider(); + assert($input instanceof InputProviderInterface); + + $session = $environment->getSessionStorage(); + assert($session instanceof SessionStorageInterface); + + $states = $session->get('LEGENDS'); $states[$input->getValue('table')][$input->getValue('legend')] = (bool) $input->getValue('state'); $session->set('LEGENDS', $states); @@ -297,6 +357,7 @@ private function getFieldName() { $environment = $this->getEnvironment(); $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); $fieldName = $inputProvider->hasValue('name') ? $inputProvider->getValue('name') : null; if (null === $fieldName) { @@ -308,8 +369,12 @@ private function getFieldName() } $dataDefinition = $environment->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + $sessionStorage = $environment->getSessionStorage(); + assert($sessionStorage instanceof SessionStorageInterface); + /** @var array{models: list} $session */ $session = $sessionStorage->get($dataDefinition->getName() . '.' . $inputProvider->getParameter('select')); $originalPropertyName = null; @@ -344,6 +409,7 @@ private function generateWidget(Widget $widget) { $environment = $this->getEnvironment(); $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); if (('select' !== $inputProvider->getParameter('act')) && ('edit' !== $inputProvider->getParameter('mode'))) { echo $widget->parse(); @@ -351,9 +417,15 @@ private function generateWidget(Widget $widget) return; } - $model = $environment->getDataProvider()->getEmptyModel(); + $dataProvider = $environment->getDataProvider(); + assert($dataProvider instanceof DataProviderInterface); + + $model = $dataProvider->getEmptyModel(); $model->setProperty($widget->name, $widget->value); - echo (new ContaoWidgetManager($environment, $model))->getWidget($inputProvider->getValue('name'))->parse(); + $widget = (new ContaoWidgetManager($environment, $model))->getWidget($inputProvider->getValue('name')); + assert($widget instanceof Widget); + + echo $widget->parse(); } } diff --git a/src/Controller/BackendTreeController.php b/src/Controller/BackendTreeController.php index a1555437..4d8f2a7f 100644 --- a/src/Controller/BackendTreeController.php +++ b/src/Controller/BackendTreeController.php @@ -24,6 +24,9 @@ use Contao\Backend; use Contao\Config; +use Contao\Controller; +use Contao\CoreBundle\Framework\ContaoFramework; +use Contao\CoreBundle\Picker\PickerBuilderInterface; use Contao\CoreBundle\Picker\PickerInterface; use Contao\Environment; use Contao\StringUtil; @@ -33,14 +36,20 @@ use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\ContaoBackendViewTemplate; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\ContaoWidgetManager; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\TreePicker; +use ContaoCommunityAlliance\DcGeneral\Data\DataProviderInterface; use ContaoCommunityAlliance\DcGeneral\Data\ModelId; use ContaoCommunityAlliance\DcGeneral\Data\PropertyValueBag; -use ContaoCommunityAlliance\DcGeneral\DcGeneral; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\Factory\DcGeneralFactory; +use ContaoCommunityAlliance\Translator\TranslatorInterface; use Symfony\Component\DependencyInjection\ContainerAwareInterface; use Symfony\Component\DependencyInjection\ContainerAwareTrait; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBagInterface; +use Symfony\Component\HttpFoundation\Session\SessionInterface; use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; use Symfony\Component\Routing\Annotation\Route; @@ -55,13 +64,6 @@ class BackendTreeController implements ContainerAwareInterface { use ContainerAwareTrait; - /** - * The DcGeneral Object. - * - * @var DcGeneral - */ - private $itemContainer; - /** * Handles the installation process. * @@ -71,9 +73,7 @@ class BackendTreeController implements ContainerAwareInterface */ public function generalTreeAction() { - $this->container->get('contao.framework')->getAdapter(\Contao\Controller::class)->setStaticUrls(); - - return $this->runBackendTree($this->container->get('request_stack')->getCurrentRequest()); + return $this->runBackendTree($this->initializeAndExtractRequest()); } /** @@ -85,9 +85,7 @@ public function generalTreeAction() */ public function generalTreeToggleAction() { - $this->container->get('contao.framework')->getAdapter(\Contao\Controller::class)->setStaticUrls(); - - return $this->runBackendTreeToggle($this->container->get('request_stack')->getCurrentRequest()); + return $this->runBackendTreeToggle($this->initializeAndExtractRequest()); } /** @@ -99,9 +97,7 @@ public function generalTreeToggleAction() */ public function generalTreeBreadCrumbAction() { - $this->container->get('contao.framework')->getAdapter(\Contao\Controller::class)->setStaticUrls(); - - return $this->runBackendTreeBreadCrumb($this->container->get('request_stack')->getCurrentRequest()); + return $this->runBackendTreeBreadCrumb($this->initializeAndExtractRequest()); } /** @@ -113,9 +109,32 @@ public function generalTreeBreadCrumbAction() */ public function generalTreeUpdateAction() { - $this->container->get('contao.framework')->getAdapter(\Contao\Controller::class)->setStaticUrls(); + return $this->runBackendTreeUpdate($this->initializeAndExtractRequest()); + } + + private function initializeAndExtractRequest(): Request + { + $container = $this->container; + assert($container instanceof ContainerInterface); + + $framework = $container->get('contao.framework'); + assert($framework instanceof ContaoFramework); + + /** + * @psalm-suppress InternalMethod + * @var Controller $controller + */ + $controller = $framework->getAdapter(Controller::class); + /** @psalm-suppress DeprecatedMethod */ + $controller->setStaticUrls(); + + $requestStack = $container->get('request_stack'); + assert($requestStack instanceof RequestStack); + + $currentRequest = $requestStack->getCurrentRequest(); + assert($currentRequest instanceof Request); - return $this->runBackendTreeUpdate($this->container->get('request_stack')->getCurrentRequest()); + return $currentRequest; } /** @@ -131,15 +150,7 @@ public function generalTreeUpdateAction() */ private function runBackendTree(Request $request) { - if (null === ($request->query->get('picker'))) { - throw new \InvalidArgumentException('No picker was given here.'); - } - $picker = $this->container->get('contao.picker.builder')->createFromData($request->query->get('picker')); - - $treeSelector = $this->prepareTreeSelector($picker); - - $sessionBag = $this->container->get('session')->getBag('contao_backend'); - $sessionBag->set($treeSelector->getSearchSessionKey(), $picker->getConfig()->getValue()); + [$value, $treeSelector] = $this->getTemplateData($request); $template = new ContaoBackendViewTemplate('be_main'); $template @@ -153,7 +164,7 @@ private function runBackendTree(Request $request) ->set('addSearch', $treeSelector->searchField) ->set('search', $GLOBALS['TL_LANG']['MSC']['search']) ->set('action', StringUtil::ampersand($request->getUri())) - ->set('value', $sessionBag->get($treeSelector->getSearchSessionKey())) + ->set('value', $value) ->set('manager', $GLOBALS['TL_LANG']['MSC']['treepickerManager'] ?? '') ->set('breadcrumb', $GLOBALS['TL_DCA'][$treeSelector->foreignTable]['list']['sorting']['breadcrumb'] ?? ''); @@ -173,21 +184,13 @@ private function runBackendTree(Request $request) */ private function runBackendTreeBreadCrumb(Request $request) { - if (null === ($request->query->get('picker'))) { - throw new \InvalidArgumentException('No picker was given here.'); - } - $picker = $this->container->get('contao.picker.builder')->createFromData($request->query->get('picker')); - - $treeSelector = $this->prepareTreeSelector($picker); - $treeSelector->generatePopup(); - - $sessionBag = $this->container->get('session')->getBag('contao_backend'); - $sessionBag->set($treeSelector->getSearchSessionKey(), $picker->getConfig()->getValue()); + [, $treeSelector] = $this->getTemplateData($request); $message = ' The bread crumb method isn´ implement yet. '; + $treeSelector->generatePopup(); $template = new ContaoBackendViewTemplate('be_main'); $template ->set('isPopup', true) @@ -215,15 +218,7 @@ private function runBackendTreeBreadCrumb(Request $request) */ private function runBackendTreeToggle(Request $request) { - if (null === ($request->query->get('picker'))) { - throw new \InvalidArgumentException('No picker was given here.'); - } - $picker = $this->container->get('contao.picker.builder')->createFromData($request->query->get('picker')); - - $treeSelector = $this->prepareTreeSelector($picker); - - $sessionBag = $this->container->get('session')->getBag('contao_backend'); - $sessionBag->set($treeSelector->getSearchSessionKey(), $picker->getConfig()->getValue()); + [, $treeSelector] = $this->getTemplateData($request); $buffer = $treeSelector->generateAjax(); @@ -251,67 +246,107 @@ private function runBackendTreeUpdate(Request $request) throw new BadRequestHttpException('This request isn`t from type ajax.'); } - if (null === ($request->query->get('picker'))) { - throw new BadRequestHttpException('No picker was given here.'); - } - $picker = $this->container->get('contao.picker.builder')->createFromData($request->query->get('picker')); + [$value, , $picker] = $this->getTemplateData($request, true); - $treeSelector = $this->prepareTreeSelector($picker); + $modelId = ModelId::fromSerialized($picker->getConfig()->getExtra('modelId')); - $sessionBag = $this->container->get('session')->getBag('contao_backend'); - $sessionBag->set( - $treeSelector->getSearchSessionKey(), - $treeSelector->widgetToValue($request->request->get('value')) - ); + $container = $this->container; + assert($container instanceof ContainerInterface); + $translator = $container->get('cca.translator.contao_translator'); + assert($translator instanceof TranslatorInterface); - $modelId = ModelId::fromSerialized($picker->getConfig()->getExtra('modelId')); + $dispatcher = $container->get('event_dispatcher'); + assert($dispatcher instanceof EventDispatcherInterface); $factory = new DcGeneralFactory(); $general = $factory ->setContainerName($modelId->getDataProviderName()) - ->setTranslator($this->container->get('cca.translator.contao_translator')) - ->setEventDispatcher($this->container->get('event_dispatcher')) + ->setTranslator($translator) + ->setEventDispatcher($dispatcher) ->createDcGeneral(); $dataProvider = $general->getEnvironment()->getDataProvider(); + assert($dataProvider instanceof DataProviderInterface); if (!($model = $dataProvider->fetch($dataProvider->getEmptyConfig()->setId($modelId->getId())))) { $model = $dataProvider->getEmptyModel(); } - $widgetValue = $treeSelector->widgetToValue($request->request->get('value')); - if (\is_array($widgetValue)) { + if (\is_array($value)) { $values = []; - // Clean keys the have empty value. - foreach ($widgetValue as $index => $value) { + // Clean keys they have empty value. + foreach ($value as $index => $val) { if ( - empty($value) + empty($val) // The first key entry has the value on, if the checkbox for all checked. - || ((0 === $index) && ('on' === $value)) + || ((0 === $index) && ('on' === $val)) ) { continue; } - $values[] = $value; + $values[] = $val; } - $widgetValue = $values; + $value = $values; } + $propertyName = $picker->getConfig()->getExtra('propertyName'); $propertyValues = new PropertyValueBag(); - $propertyValues->setPropertyValue($picker->getConfig()->getExtra('propertyName'), $widgetValue); - $general->getEnvironment()->getController()->updateModelFromPropertyBag($model, $propertyValues); + $propertyValues->setPropertyValue($propertyName, $value); + + $controller = $general->getEnvironment()->getController(); + assert($controller instanceof ControllerInterface); + + $controller->updateModelFromPropertyBag($model, $propertyValues); $widgetManager = new ContaoWidgetManager($general->getEnvironment(), $model); - $buffer = - $widgetManager->renderWidget($picker->getConfig()->getExtra('propertyName'), false, $propertyValues); + $buffer = $widgetManager->renderWidget($propertyName, false, $propertyValues); $response = new Response($buffer); - $response->headers->set('Content-Type', 'txt/html; charset=' . Config::get('characterSet')); + $response->headers->set('Content-Type', 'txt/html; charset=' . (Config::get('characterSet') ?? '')); return $response; } + /** + * @param Request $request The request to obtain information from. + * @param bool $valueFromRequest Flag if the value shall be read from the request. + * + * @return array{0: string|list, 1: TreePicker, 2: PickerInterface} + */ + private function getTemplateData(Request $request, bool $valueFromRequest = false): array + { + if ('' === ($getPicker = (string) $request->query->get('picker'))) { + throw new BadRequestHttpException('No picker was given here.'); + } + $container = $this->container; + assert($container instanceof ContainerInterface); + $pickerBuilder = $container->get('contao.picker.builder'); + assert($pickerBuilder instanceof PickerBuilderInterface); + $picker = $pickerBuilder->createFromData($getPicker); + assert($picker instanceof PickerInterface); + $treeSelector = $this->prepareTreeSelector($picker); + $session = $container->get('session'); + assert($session instanceof SessionInterface); + $sessionBag = $session->getBag('contao_backend'); + assert($sessionBag instanceof AttributeBagInterface); + $value = $picker->getConfig()->getValue(); + if ($valueFromRequest) { + if (null !== $reqValue = $request->request->get('value')) { + $reqValue = (string) $reqValue; + } + $value = $treeSelector->widgetToValue($reqValue); + } + + $sessionBag->set($treeSelector->getSearchSessionKey(), $value); + + return [ + $sessionBag->get($treeSelector->getSearchSessionKey()), + $treeSelector, + $picker + ]; + } + /** * Prepare the tree selector. * @@ -335,21 +370,35 @@ private function prepareTreeSelector(PickerInterface $picker) throw new \InvalidArgumentException('The field name contains invalid characters'); } - $sessionBag = $this->container->get('session')->getBag('contao_backend'); + $container = $this->container; + assert($container instanceof ContainerInterface); + + $session = $container->get('session'); + assert($session instanceof SessionInterface); + + $sessionBag = $session->getBag('contao_backend'); + assert($sessionBag instanceof AttributeBagInterface); + // Define the current ID. \define('CURRENT_ID', ($table ? $sessionBag->get('CURRENT_ID') : $modelId->getId())); - $this->itemContainer = (new DcGeneralFactory()) + $translator = $container->get('cca.translator.contao_translator'); + assert($translator instanceof TranslatorInterface); + + $dispatcher = $container->get('event_dispatcher'); + assert($dispatcher instanceof EventDispatcherInterface); + + $itemContainer = (new DcGeneralFactory()) ->setContainerName($modelId->getDataProviderName()) - ->setTranslator($this->container->get('cca.translator.contao_translator')) - ->setEventDispatcher($this->container->get('event_dispatcher')) + ->setTranslator($translator) + ->setEventDispatcher($dispatcher) ->createDcGeneral(); + $definition = $itemContainer->getEnvironment()->getDataDefinition(); + assert($definition instanceof ContainerInterface); + // Merge with the information from the data container. - $property = $this - ->itemContainer - ->getEnvironment() - ->getDataDefinition() + $property = $definition ->getPropertiesDefinition() ->getProperty($picker->getConfig()->getExtra('propertyName')); @@ -359,16 +408,20 @@ private function prepareTreeSelector(PickerInterface $picker) } $information['eval'] = array_merge($property->getExtra(), $information['eval']); - $treeSelector = new $GLOBALS['BE_FFL']['DcGeneralTreePicker']( + $dcCompat = new DcCompat($itemContainer->getEnvironment()); + /** @var class-string $class */ + $class = $GLOBALS['BE_FFL']['DcGeneralTreePicker']; + /** @psalm-suppress UnsafeInstantiation - No other way to instantiate. */ + $treeSelector = new $class( Widget::getAttributesFromDca( $information, $field, \array_filter(\explode(',', $picker->getConfig()->getValue())), $field, $table, - new DcCompat($this->itemContainer->getEnvironment()) + $dcCompat ), - new DcCompat($this->itemContainer->getEnvironment()) + $dcCompat ); $treeSelector->id = 'tl_listing'; diff --git a/src/Controller/ControllerInterface.php b/src/Controller/ControllerInterface.php index d8bf48ec..8b309bf9 100644 --- a/src/Controller/ControllerInterface.php +++ b/src/Controller/ControllerInterface.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -15,7 +15,7 @@ * @author Tristan Lins * @author David Molineus * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -53,7 +53,7 @@ public function setEnvironment(EnvironmentInterface $environment); public function getEnvironment(); /** - * Handle a action within this environment. + * Handle an action within this environment. * * @param Action $action The action to be executed. * @@ -95,8 +95,8 @@ public function searchParentOf(ModelInterface $model); * (or originating table of the model, if no provider name has been given) for all levels and parent child * conditions. * - * @param ModelInterface $objModel The model to assemble children from. - * @param string $strDataProvider The name of the data provider to fetch children from. + * @param ModelInterface $model The model to assemble children from. + * @param string $providerName The name of the data provider to fetch children from. * * @return array * @@ -104,7 +104,7 @@ public function searchParentOf(ModelInterface $model); * * @see \ContaoCommunityAlliance\DcGeneral\Controller\ModelCollector::collectChildrenOf(). */ - public function assembleAllChildrenFrom($objModel, $strDataProvider = ''); + public function assembleAllChildrenFrom($model, $providerName = ''); /** * Update the current model from a post request. Additionally, trigger meta palettes, if installed. @@ -171,9 +171,9 @@ public function getModelFromClipboardItem(ItemInterface $item); /** * Retrieve models from the clipboard items. * - * @param array|ItemInterface[] $items The clipboard items. + * @param list $items The clipboard items. * - * @return CollectionInterface|ModelInterface[] + * @return CollectionInterface */ public function getModelsFromClipboardItems(array $items); @@ -184,21 +184,21 @@ public function getModelsFromClipboardItems(array $items); * * @param ModelIdInterface $parentModelId The optional parent id. If not given, the models must not have a parent. * - * @return CollectionInterface|\ContaoCommunityAlliance\DcGeneral\Data\ModelInterface[] + * @return CollectionInterface */ public function getModelsFromClipboard(ModelIdInterface $parentModelId = null); /** * Evaluate clipboard items, then return the corresponding models. * - * @param ModelIdInterface $source The source model id. - * @param ModelIdInterface $after The previous model id. - * @param ModelIdInterface $into The hierarchical parent model id. - * @param ModelIdInterface $parentModelId The parent model id. - * @param FilterInterface $filter Clipboard filter. - * @param array $items Write-back evaluated clipboard items. + * @param ModelIdInterface|null $source The source model id. + * @param ModelIdInterface|null $after The previous model id. + * @param ModelIdInterface|null $into The hierarchical parent model id. + * @param ModelIdInterface|null $parentModelId The parent model id. + * @param FilterInterface|null $filter Clipboard filter. + * @param array $items Write-back evaluated clipboard items. * - * @return CollectionInterface|ModelInterface[] + * @return CollectionInterface */ public function applyClipboardActions( ModelIdInterface $source = null, @@ -212,9 +212,9 @@ public function applyClipboardActions( /** * Paste the content of the clipboard onto the top. * - * @param CollectionInterface $models The models to be inserted. - * @param string $sortedBy The name of the sorting property. - * @param ModelIdInterface $parentId The parent model ID. + * @param CollectionInterface $models The models to be inserted. + * @param string $sortedBy The name of the sorting property. + * @param ModelIdInterface|null $parentId The parent model ID. * * @return void */ diff --git a/src/Controller/DefaultController.php b/src/Controller/DefaultController.php index b9ed23a1..c89b49ae 100644 --- a/src/Controller/DefaultController.php +++ b/src/Controller/DefaultController.php @@ -33,6 +33,8 @@ namespace ContaoCommunityAlliance\DcGeneral\Controller; use ContaoCommunityAlliance\DcGeneral\Action; +use ContaoCommunityAlliance\DcGeneral\BaseConfigRegistryInterface; +use ContaoCommunityAlliance\DcGeneral\Clipboard\ClipboardInterface; use ContaoCommunityAlliance\DcGeneral\Clipboard\Filter; use ContaoCommunityAlliance\DcGeneral\Clipboard\FilterInterface; use ContaoCommunityAlliance\DcGeneral\Clipboard\Item; @@ -48,6 +50,7 @@ use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; use ContaoCommunityAlliance\DcGeneral\Data\ModelManipulator; use ContaoCommunityAlliance\DcGeneral\Data\MultiLanguageDataProviderInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\BasicDefinitionInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\Properties\PropertyInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\GroupAndSortingInformationInterface; @@ -61,6 +64,8 @@ use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralInvalidArgumentException; use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralRuntimeException; use ContaoCommunityAlliance\DcGeneral\Factory\DcGeneralFactory; +use ContaoCommunityAlliance\Translator\TranslatorInterface; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; /** * This class serves as main controller class in dc general. @@ -73,6 +78,7 @@ * @SuppressWarnings(PHPMD.TooManyFields) * @SuppressWarnings(PHPMD.TooManyMethods) * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * @psalm-suppress MissingConstructor */ class DefaultController implements ControllerInterface { @@ -133,11 +139,17 @@ public function __call($name, $arguments) */ public function setEnvironment(EnvironmentInterface $environment) { - $this->environment = $environment; - $definition = $environment->getDataDefinition(); + $this->environment = $environment; + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $mode = $definition->getBasicDefinition()->getMode(); + assert(\is_int($mode)); + $this->relationshipManager = new RelationshipManager( $definition->getModelRelationshipDefinition(), - $definition->getBasicDefinition()->getMode() + $mode ); $this->modelCollector = new ModelCollector($this->environment); @@ -158,9 +170,13 @@ public function getEnvironment() public function handle(Action $action) { $event = new ActionEvent($this->getEnvironment(), $action); - $this->getEnvironment()->getEventDispatcher()->dispatch($event, DcGeneralEvents::ACTION); - return $event->getResponse(); + $dispatcher = $this->getEnvironment()->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $dispatcher->dispatch($event, DcGeneralEvents::ACTION); + + return (string) $event->getResponse(); } /** @@ -168,7 +184,7 @@ public function handle(Action $action) * * @deprecated Use \ContaoCommunityAlliance\DcGeneral\Controller\ModelCollector::searchParentOfIn(). * - * @see \ContaoCommunityAlliance\DcGeneral\Controller\ModelCollector::searchParentOfIn(). + * @see ModelCollector::searchParentOfIn */ public function searchParentOfIn(ModelInterface $model, CollectionInterface $models) { @@ -179,7 +195,12 @@ public function searchParentOfIn(ModelInterface $model, CollectionInterface $mod ); // @codingStandardsIgnoreEnd - return $this->modelCollector->searchParentOfIn($model, $models); + $parent = $this->modelCollector->searchParentOfIn($model, $models); + if (null === $parent) { + throw new \RuntimeException('Not found'); + } + + return $parent; } /** @@ -189,7 +210,7 @@ public function searchParentOfIn(ModelInterface $model, CollectionInterface $mod * * @deprecated Use \ContaoCommunityAlliance\DcGeneral\Controller\ModelCollector::searchParentOf(). * - * @see \ContaoCommunityAlliance\DcGeneral\Controller\ModelCollector::searchParentOf(). + * @see ModelCollector::searchParentOf */ public function searchParentOf(ModelInterface $model) { @@ -200,7 +221,12 @@ public function searchParentOf(ModelInterface $model) ); // @codingStandardsIgnoreEnd - return $this->modelCollector->searchParentOf($model); + $parent = $this->modelCollector->searchParentOf($model); + if (null === $parent) { + throw new \RuntimeException('Not found'); + } + + return $parent; } /** @@ -208,7 +234,7 @@ public function searchParentOf(ModelInterface $model) * * @deprecated Use \ContaoCommunityAlliance\DcGeneral\Controller\ModelCollector::collectChildrenOf(). * - * @see \ContaoCommunityAlliance\DcGeneral\Controller\ModelCollector::collectChildrenOf(). + * @see ModelCollector::collectChildrenOf */ public function assembleAllChildrenFrom($model, $providerName = '') { @@ -225,17 +251,15 @@ public function assembleAllChildrenFrom($model, $providerName = '') /** * Retrieve all siblings of a given model. * - * @param ModelInterface $model The model for which the siblings shall be retrieved from. - * @param string|null $sortingProperty The property name to use for sorting. - * @param ModelIdInterface $parentId The (optional) parent id to use. + * @param ModelInterface $model The model for which the siblings shall be retrieved from. + * @param null $sortingProperty The property name to use for sorting. + * @param ModelIdInterface|null $parentId The (optional) parent id to use. * * @return CollectionInterface * - * @throws DcGeneralRuntimeException When no parent model can be located. - * * @deprecated Use ContaoCommunityAlliance\DcGeneral\Controller\ModelCollector::collectSiblingsOf(). * - * @see \ContaoCommunityAlliance\DcGeneral\Controller\ModelCollector::collectSiblingsOf(). + * @see ModelCollector::collectSiblingsOf */ protected function assembleSiblingsFor( ModelInterface $model, @@ -260,15 +284,23 @@ protected function assembleSiblingsFor( * * @return CollectionInterface * - * @throws DcGeneralRuntimeException Unable to retrieve children in non hierarchical mode. + * @throws DcGeneralRuntimeException Unable to retrieve children in non-hierarchical mode. * @throws DcGeneralInvalidArgumentException Invalid configuration. Child condition must be defined. */ protected function assembleChildrenFor(ModelInterface $model, $sortingProperty = null) { - $environment = $this->getEnvironment(); - $definition = $environment->getDataDefinition(); - $provider = $environment->getDataProvider($model->getProviderName()); - $config = $environment->getBaseConfigRegistry()->getBaseConfig(); + $environment = $this->getEnvironment(); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $provider = $environment->getDataProvider($model->getProviderName()); + assert($provider instanceof DataProviderInterface); + + $registry = $environment->getBaseConfigRegistry(); + assert($registry instanceof BaseConfigRegistryInterface); + + $config = $registry->getBaseConfig(); $relationships = $definition->getModelRelationshipDefinition(); if (BasicDefinitionInterface::MODE_HIERARCHICAL !== $definition->getBasicDefinition()->getMode()) { @@ -284,10 +316,12 @@ protected function assembleChildrenFor(ModelInterface $model, $sortingProperty = $config->setFilter($condition->getFilter($model)); if ($sortingProperty) { - $config->setSorting([(string) $sortingProperty => 'ASC']); + $config->setSorting([$sortingProperty => 'ASC']); } - return $provider->fetchAll($config); + $childrenCollection = $provider->fetchAll($config); + assert($childrenCollection instanceof CollectionInterface); + return $childrenCollection; } /** @@ -295,11 +329,12 @@ protected function assembleChildrenFor(ModelInterface $model, $sortingProperty = */ public function updateModelFromPropertyBag($model, $propertyValues) { - if (!$propertyValues) { - return $this; - } $environment = $this->getEnvironment(); - $properties = $environment->getDataDefinition()->getPropertiesDefinition(); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $properties = $definition->getPropertiesDefinition(); ModelManipulator::updateModelFromPropertyBag($properties, $model, $propertyValues); @@ -309,18 +344,18 @@ public function updateModelFromPropertyBag($model, $propertyValues) /** * Return all supported languages from the default data data provider. * - * @param mixed $currentID The id of the item for which to retrieve the valid languages. + * @param mixed $mixID The id of the item for which to retrieve the valid languages. * * @return array */ - public function getSupportedLanguages($currentID) + public function getSupportedLanguages($mixID) { $environment = $this->getEnvironment(); $dataProvider = $environment->getDataProvider(); // Check if current data provider supports multi language. if ($dataProvider instanceof MultiLanguageDataProviderInterface) { - $supportedLanguages = $dataProvider->getLanguages($currentID); + $supportedLanguages = $dataProvider->getLanguages($mixID); } else { $supportedLanguages = null; } @@ -331,6 +366,7 @@ public function getSupportedLanguages($currentID) } $translator = $environment->getTranslator(); + assert($translator instanceof TranslatorInterface); // Make an array from the collection. $languages = []; @@ -388,9 +424,14 @@ public function createClonedModel($model) $clone = clone $model; $clone->setId(null); - $environment = $this->getEnvironment(); - $properties = $environment->getDataDefinition()->getPropertiesDefinition(); + $environment = $this->getEnvironment(); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $properties = $definition->getPropertiesDefinition(); $dataProvider = $environment->getDataProvider($clone->getProviderName()); + assert($dataProvider instanceof DataProviderInterface); foreach (\array_keys($clone->getPropertiesAsArray()) as $propName) { // If the property is not known, remove it. @@ -412,7 +453,7 @@ public function createClonedModel($model) * * @deprecated Use \ContaoCommunityAlliance\DcGeneral\Controller\ModelCollector::getModel(). * - * @see \ContaoCommunityAlliance\DcGeneral\Controller\ModelCollector::getModel(). + * @see ModelCollector::getModel */ public function fetchModelFromProvider($modelId, $providerName = null) { @@ -423,7 +464,12 @@ public function fetchModelFromProvider($modelId, $providerName = null) ); // @codingStandardsIgnoreEnd - return $this->modelCollector->getModel($modelId, $providerName); + $model = $this->modelCollector->getModel($modelId, $providerName); + if (null === $model) { + throw new \RuntimeException('Not found'); + } + + return $model; } /** @@ -431,9 +477,14 @@ public function fetchModelFromProvider($modelId, $providerName = null) */ public function createEmptyModelWithDefaults() { - $environment = $this->getEnvironment(); - $definition = $environment->getDataDefinition(); - $dataProvider = $environment->getDataProvider(); + $environment = $this->getEnvironment(); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $dataProvider = $environment->getDataProvider(); + assert($dataProvider instanceof DataProviderInterface); + $propertyDefinition = $definition->getPropertiesDefinition(); $properties = $propertyDefinition->getProperties(); $model = $dataProvider->getEmptyModel(); @@ -481,7 +532,10 @@ public function getModelsFromClipboardItems(array $items) continue; } - $models->push($environment->getDataProvider($item->getDataProviderName())->getEmptyModel()); + $dataProvider = $environment->getDataProvider($item->getDataProviderName()); + assert($dataProvider instanceof DataProviderInterface); + + $models->push($dataProvider->getEmptyModel()); } return $models; @@ -492,11 +546,17 @@ public function getModelsFromClipboardItems(array $items) */ public function getModelsFromClipboard(ModelIdInterface $parentModelId = null) { - $environment = $this->getEnvironment(); - $dataDefinition = $environment->getDataDefinition(); + $environment = $this->getEnvironment(); + + $dataDefinition = $environment->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + $basicDefinition = $dataDefinition->getBasicDefinition(); $modelProviderName = $basicDefinition->getDataProvider(); - $clipboard = $environment->getClipboard(); + assert(\is_string($modelProviderName)); + + $clipboard = $environment->getClipboard(); + assert($clipboard instanceof ClipboardInterface); $filter = new Filter(); $filter->andModelIsFromProvider($modelProviderName); @@ -541,67 +601,87 @@ public function applyClipboardActions( */ private function getActionsFromSource(ModelIdInterface $source, ModelIdInterface $parentModelId = null) { - $basicDefinition = $this->getEnvironment()->getDataDefinition()->getBasicDefinition(); + $definition = $this->getEnvironment()->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $basicDefinition = $definition->getBasicDefinition(); + assert($basicDefinition instanceof BasicDefinitionInterface); + + $dataProvider = $basicDefinition->getDataProvider(); + assert(\is_string($dataProvider)); $filter = new Filter(); - $filter->andModelIsFromProvider($basicDefinition->getDataProvider()); + $filter->andModelIsFromProvider($dataProvider); if ($basicDefinition->getParentDataProvider()) { - $filter->andParentIsFromProvider($basicDefinition->getParentDataProvider()); + $parentDataProvider = $basicDefinition->getDataProvider(); + assert(\is_string($parentDataProvider)); + + $filter->andParentIsFromProvider($parentDataProvider); } else { $filter->andHasNoParent(); } $filter->andModelIs($source); - $item = $this->getEnvironment()->getClipboard()->fetch($filter)[0] ?? null; + $clipboard = $this->getEnvironment()->getClipboard(); + assert($clipboard instanceof ClipboardInterface); - $action = $item ? $item->getAction() : ItemInterface::CUT; - $model = $this->modelCollector->getModel($source); - $actions = [ + $item = $clipboard->fetch($filter)[0] ?? null; + + $action = $item ? $item->getAction() : ItemInterface::CUT; + $model = $this->modelCollector->getModel($source); + assert($model instanceof ModelInterface); + + return [ [ 'model' => $model, 'item' => new Item($action, $parentModelId, ModelId::fromModel($model)) ] ]; - - return $actions; } /** * Fetch actions from the clipboard. * - * @param FilterInterface|null $filter The clipboard filter. - * @param ModelIdInterface $parentModelId The parent id. + * @param FilterInterface|null $filter The clipboard filter. + * @param ModelIdInterface|null $parentModelId The parent id. * * @return array */ private function fetchModelsFromClipboard(FilterInterface $filter = null, ModelIdInterface $parentModelId = null) { - $environment = $this->getEnvironment(); + $environment = $this->getEnvironment(); + assert($environment instanceof EnvironmentInterface); + $dataDefinition = $environment->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); if (!$filter) { $filter = new Filter(); } - $basicDefinition = $dataDefinition->getBasicDefinition(); - $modelProviderName = $basicDefinition->getDataProvider(); - $filter->andModelIsFromProvider($modelProviderName); - if ($parentModelId) { - $filter->andParentIsFromProvider($parentModelId->getDataProviderName()); - } else { - $filter->andHasNoParent(); + if ($filter instanceof Filter) { + $basicDefinition = $dataDefinition->getBasicDefinition(); + $modelProviderName = $basicDefinition->getDataProvider(); + assert(\is_string($modelProviderName)); + $filter->andModelIsFromProvider($modelProviderName); + if ($parentModelId) { + $filter->andParentIsFromProvider($parentModelId->getDataProviderName()); + } else { + $filter->andHasNoParent(); + } } - $environment = $this->getEnvironment(); - $clipboard = $environment->getClipboard(); - $items = $clipboard->fetch($filter); - $actions = []; + $clipboard = $environment->getClipboard(); + assert($clipboard instanceof ClipboardInterface); + + $items = $clipboard->fetch($filter); + $actions = []; foreach ($items as $item) { $model = null; - if (!$item->isCreate() && $item->getModelId()) { - $model = $this->modelCollector->getModel($item->getModelId()->getId(), $item->getDataProviderName()); + if (!$item->isCreate() && null !== ($model = $item->getModelId())) { + $model = $this->modelCollector->getModel((string) $model->getId(), $item->getDataProviderName()); } $actions[] = [ @@ -616,11 +696,11 @@ private function fetchModelsFromClipboard(FilterInterface $filter = null, ModelI /** * Effectively do the actions. * - * @param array $actions The actions collection. - * @param ModelIdInterface $after The previous model id. - * @param ModelIdInterface $into The hierarchical parent model id. - * @param ModelIdInterface $parentModelId The parent model id. - * @param array $items Write-back clipboard items. + * @param array $actions The action's collection. + * @param ModelIdInterface|null $after The previous model id. + * @param ModelIdInterface|null $into The hierarchical parent model id. + * @param ModelIdInterface|null $parentModelId The parent model id. + * @param array $items Write-back clipboard items. * * @return CollectionInterface */ @@ -637,22 +717,22 @@ private function doActions( $parentModel = null; } - // Holds models, that need deep-copy + // Holds models, that need deep-copy. $deepCopyList = []; - // Apply create and copy actions + // Apply to create and copy actions. foreach ($actions as &$action) { $this->applyAction($action, $deepCopyList, $parentModel); } unset($action); - // When pasting after another model, apply same grouping information + // When pasting after another model, apply same grouping information. $this->ensureSameGrouping($actions, $after); - // Now apply sorting and persist all models + // Now apply sorting and persist all models. $models = $this->sortAndPersistModels($actions, $after, $into, $parentModelId, $items); - // At least, go ahead with the deep copy + // At least, go ahead with the deep copy. $this->doDeepCopy($deepCopyList); return $models; @@ -663,13 +743,12 @@ private function doActions( * * This will create or clone the model in the action. * - * @param array $action The action, containing a model and an item. - * @param array $deepCopyList A list of models that need deep copy. - * @param ModelInterface $parentModel The parent model. + * @param array $action The action, containing a model and an item. + * @param array $deepCopyList A list of models that need deep copy. + * @param ModelInterface|null $parentModel The parent model. * * @return void * - * @throws \UnexpectedValueException When the action is neither create, copy or deep copy. */ private function applyAction(array &$action, array &$deepCopyList, ModelInterface $parentModel = null) { @@ -683,8 +762,10 @@ private function applyAction(array &$action, array &$deepCopyList, ModelInterfac // create new model $model = $this->createEmptyModelWithDefaults(); } elseif ($item->isCopy() || $isDeepCopy = $item->isDeepCopy()) { + assert($model instanceof ModelInterface); // copy model - $model = $this->modelCollector->getModel(ModelId::fromModel($model)); + $model = $this->modelCollector->getModel(ModelId::fromModel($model)); + assert($model instanceof ModelInterface); $clonedModel = $this->doCloneAction($model); if ($isDeepCopy) { @@ -721,16 +802,19 @@ private function doCloneAction(ModelInterface $model) { $environment = $this->getEnvironment(); + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + // Make a duplicate. $clonedModel = $this->createClonedModel($model); // Trigger the pre duplicate event. $duplicateEvent = new PreDuplicateModelEvent($environment, $clonedModel, $model); - $environment->getEventDispatcher()->dispatch($duplicateEvent, $duplicateEvent::NAME); + $dispatcher->dispatch($duplicateEvent, $duplicateEvent::NAME); // And trigger the post event for it. $duplicateEvent = new PostDuplicateModelEvent($environment, $clonedModel, $model); - $environment->getEventDispatcher()->dispatch($duplicateEvent, $duplicateEvent::NAME); + $dispatcher->dispatch($duplicateEvent, $duplicateEvent::NAME); return $clonedModel; } @@ -738,8 +822,8 @@ private function doCloneAction(ModelInterface $model) /** * Ensure all models have the same grouping. * - * @param array $actions The actions collection. - * @param ModelIdInterface $after The previous model id. + * @param array $actions The action's collection. + * @param ModelIdInterface|null $after The previous model id. * * @return void */ @@ -751,6 +835,8 @@ private function ensureSameGrouping(array $actions, ModelIdInterface $after = nu // when pasting after another item, inherit the grouping field $groupingField = $groupingMode['property']; $previous = $this->modelCollector->getModel($after); + assert($previous instanceof ModelInterface); + $groupingValue = $previous->getProperty($groupingField); foreach ($actions as $action) { @@ -764,15 +850,14 @@ private function ensureSameGrouping(array $actions, ModelIdInterface $after = nu /** * Apply sorting and persist all models. * - * @param array $actions The actions collection. - * @param ModelIdInterface $after The previous model id. - * @param ModelIdInterface $into The hierarchical parent model id. - * @param ModelIdInterface $parentModelId The parent model id. - * @param array $items Write-back clipboard items. + * @param array $actions The actions collection. + * @param ModelIdInterface|null $after The previous model id. + * @param ModelIdInterface|null $into The hierarchical parent model id. + * @param ModelIdInterface|null $parentModelId The parent model id. + * @param array $items Write-back clipboard items. * - * @return DefaultCollection|ModelInterface[] + * @return DefaultCollection * - * @throws DcGeneralRuntimeException When the parameters for the pasting destination are invalid. */ private function sortAndPersistModels( array $actions, @@ -781,8 +866,8 @@ private function sortAndPersistModels( ModelIdInterface $parentModelId = null, array &$items = [] ) { - /** @var DefaultCollection|ModelInterface[] $models */ $models = $this->createModelCollectionFromActions($actions, $items); + assert($models instanceof CollectionInterface); $this->triggerPrePasteModel($models); @@ -801,8 +886,8 @@ private function sortAndPersistModels( /** * Process paste the collection of models after the a model. * - * @param CollectionInterface $models The collection of models. - * @param ModelIdInterface $after The paste after model. + * @param CollectionInterface $models The collection of models. + * @param ModelIdInterface|null $after The paste after model. * * @return void */ @@ -810,8 +895,12 @@ private function processPasteAfter(CollectionInterface $models, ModelIdInterface { if ($after && $models->count() && $after->getId()) { $manualSorting = ViewHelpers::getManualSortingProperty($this->getEnvironment()); + assert(\is_string($manualSorting)); + + $model = $this->modelCollector->getModel($after); + assert($model instanceof ModelInterface); - $this->pasteAfter($this->modelCollector->getModel($after), $models, $manualSorting); + $this->pasteAfter($model, $models, $manualSorting); $this->triggerPostPasteModel($models); $this->clearModelCollection($models); @@ -819,10 +908,10 @@ private function processPasteAfter(CollectionInterface $models, ModelIdInterface } /** - * Process paste the collection of models into the a model. + * Process paste the collection of models into the model. * - * @param CollectionInterface $models The collection of models. - * @param ModelIdInterface $into The paste into model. + * @param CollectionInterface $models The collection of models. + * @param ModelIdInterface|null $into The paste into model. * * @return void */ @@ -830,8 +919,12 @@ private function processPasteInto(CollectionInterface $models, ModelIdInterface { if ($into && $models->count() && $into->getId()) { $manualSorting = ViewHelpers::getManualSortingProperty($this->getEnvironment()); + assert(\is_string($manualSorting)); + + $model = $this->modelCollector->getModel($into); + assert($model instanceof ModelInterface); - $this->pasteInto($this->modelCollector->getModel($into), $models, $manualSorting); + $this->pasteInto($model, $models, $manualSorting); $this->triggerPostPasteModel($models); $this->clearModelCollection($models); @@ -841,10 +934,10 @@ private function processPasteInto(CollectionInterface $models, ModelIdInterface /** * Process paste the content of the clipboard onto the top after a model without reference. * - * @param CollectionInterface $models The collection of models. - * @param ModelIdInterface $after The previous model id. - * @param ModelIdInterface $into The hierarchical parent model id. - * @param ModelIdInterface $parent The parent model id. + * @param CollectionInterface $models The collection of models. + * @param ModelIdInterface|null $after The previous model id. + * @param ModelIdInterface|null $into The hierarchical parent model id. + * @param ModelIdInterface|null $parent The parent model id. * * @return void */ @@ -859,8 +952,11 @@ private function processPasteTopWithoutReference( && (($after && (0 === (int) $after->getId())) || ($into && (0 === (int) $into->getId()))) ) { - $manualSorting = ViewHelpers::getManualSortingProperty($this->getEnvironment()); + $manualSorting = ViewHelpers::getManualSortingProperty($this->getEnvironment()); + assert(\is_string($manualSorting)); + $dataDefinition = $this->getEnvironment()->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); if (BasicDefinitionInterface::MODE_HIERARCHICAL === $dataDefinition->getBasicDefinition()->getMode()) { $this->relationshipManager->setAllRoot($models); @@ -893,6 +989,8 @@ private function processPasteTopAfterModel(CollectionInterface $models, ModelIdI } $dataProvider = $this->getEnvironment()->getDataProvider(); + assert($dataProvider instanceof DataProviderInterface); + $dataProvider->saveEach($models); $this->triggerPostPasteModel($models); @@ -930,7 +1028,11 @@ private function triggerPrePasteModel(CollectionInterface $collection) { foreach ($collection as $model) { $event = new PrePasteModelEvent($this->getEnvironment(), $model); - $this->getEnvironment()->getEventDispatcher()->dispatch($event, $event::NAME); + + $dispatcher = $this->getEnvironment()->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $dispatcher->dispatch($event, $event::NAME); } } @@ -945,7 +1047,11 @@ private function triggerPostPasteModel(CollectionInterface $collection) { foreach ($collection as $model) { $event = new PostPasteModelEvent($this->getEnvironment(), $model); - $this->getEnvironment()->getEventDispatcher()->dispatch($event, $event::NAME); + + $dispatcher = $this->getEnvironment()->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $dispatcher->dispatch($event, $event::NAME); } } @@ -964,8 +1070,11 @@ protected function doDeepCopy(array $deepCopyList) return; } - $factory = DcGeneralFactory::deriveFromEnvironment($this->getEnvironment()); - $dataDefinition = $this->getEnvironment()->getDataDefinition(); + $factory = DcGeneralFactory::deriveFromEnvironment($this->getEnvironment()); + + $dataDefinition = $this->getEnvironment()->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + $modelRelationshipDefinition = $dataDefinition->getModelRelationshipDefinition(); $childConditions = $modelRelationshipDefinition->getChildConditions($dataDefinition->getName()); @@ -981,13 +1090,22 @@ protected function doDeepCopy(array $deepCopyList) // create new destination environment $destinationName = $childCondition->getDestinationName(); $factory->setContainerName($destinationName); - $destinationEnvironment = $factory->createEnvironment(); + $destinationEnvironment = $factory->createEnvironment(); + $destinationDataDefinition = $destinationEnvironment->getDataDefinition(); + assert($destinationDataDefinition instanceof ContainerInterface); + $destinationViewDefinition = $destinationDataDefinition->getDefinition( Contao2BackendViewDefinitionInterface::NAME ); - $destinationDataProvider = $destinationEnvironment->getDataProvider(); - $destinationController = $destinationEnvironment->getController(); + assert($destinationViewDefinition instanceof Contao2BackendViewDefinitionInterface); + + $destinationDataProvider = $destinationEnvironment->getDataProvider(); + assert($destinationDataProvider instanceof DataProviderInterface); + + $destinationController = $destinationEnvironment->getController(); + assert($destinationController instanceof ControllerInterface); + /** @var Contao2BackendViewDefinitionInterface $destinationViewDefinition */ /** @var DefaultController $destinationController */ $listingConfig = $destinationViewDefinition->getListingConfig(); @@ -1017,6 +1135,10 @@ protected function doDeepCopy(array $deepCopyList) // build the copy actions foreach ($children as $childModel) { + if (!($childModel instanceof ModelInterface)) { + continue; + } + $childModelId = ModelId::fromModel($childModel); $actions[] = [ @@ -1044,11 +1166,18 @@ public function pasteTop(CollectionInterface $models, $sortedBy, ModelIdInterfac { $environment = $this->getEnvironment(); + $firstModel = $models->get(0); + assert($firstModel instanceof ModelInterface); + // Enforce proper sorting now. - $siblings = $this->modelCollector->collectSiblingsOf($models->get(0), $sortedBy, $parentId); + $siblings = $this->modelCollector->collectSiblingsOf($firstModel, $sortedBy, $parentId); $newList = (new SortingManager($models, $siblings, $sortedBy, null))->getResults(); + assert($newList instanceof CollectionInterface); + + $dataProvider = $environment->getDataProvider($firstModel->getProviderName()); + assert($dataProvider instanceof DataProviderInterface); - $environment->getDataProvider($models->get(0)->getProviderName())->saveEach($newList); + $dataProvider->saveEach($newList); } /** @@ -1061,11 +1190,15 @@ public function pasteAfter(ModelInterface $previousModel, CollectionInterface $m if (0 === $models->length()) { throw new \RuntimeException('No models passed to pasteAfter().'); } + $environment = $this->getEnvironment(); + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + if ( \in_array( - $environment->getDataDefinition()->getBasicDefinition()->getMode(), + $definition->getBasicDefinition()->getMode(), [ BasicDefinitionInterface::MODE_HIERARCHICAL, BasicDefinitionInterface::MODE_PARENTEDLIST @@ -1074,6 +1207,8 @@ public function pasteAfter(ModelInterface $previousModel, CollectionInterface $m ) { if (!$this->relationshipManager->isRoot($previousModel)) { $parentModel = $this->modelCollector->searchParentOf($previousModel); + assert($parentModel instanceof ModelInterface); + $parentName = $parentModel->getProviderName(); $this->relationshipManager->setSameParentForAll($models, $previousModel, $parentName); } else { @@ -1084,8 +1219,12 @@ public function pasteAfter(ModelInterface $previousModel, CollectionInterface $m // Enforce proper sorting now. $siblings = $this->modelCollector->collectSiblingsOf($previousModel, $sortedBy); $newList = (new SortingManager($models, $siblings, $sortedBy, $previousModel))->getResults(); + assert($newList instanceof CollectionInterface); + + $dataProvider = $environment->getDataProvider($previousModel->getProviderName()); + assert($dataProvider instanceof DataProviderInterface); - $environment->getDataProvider($previousModel->getProviderName())->saveEach($newList); + $dataProvider->saveEach($newList); } /** @@ -1100,8 +1239,15 @@ public function pasteInto(ModelInterface $parentModel, CollectionInterface $mode // Enforce proper sorting now. $siblings = $this->assembleChildrenFor($parentModel, $sortedBy); $newList = (new SortingManager($models, $siblings, $sortedBy))->getResults(); + assert($newList instanceof CollectionInterface); + + $firstItem = $newList->get(0); + assert($firstItem instanceof ModelInterface); + + $dataProvider = $environment->getDataProvider($firstItem->getProviderName()); + assert($dataProvider instanceof DataProviderInterface); - $environment->getDataProvider($newList->get(0)->getProviderName())->saveEach($newList); + $dataProvider->saveEach($newList); } /** diff --git a/src/Controller/ModelCollector.php b/src/Controller/ModelCollector.php index 5babbb76..99c4b25a 100644 --- a/src/Controller/ModelCollector.php +++ b/src/Controller/ModelCollector.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2022 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -16,13 +16,14 @@ * @author Ingolf Steinhardt * @author David Molineus * @author Stefan Heimes - * @copyright 2013-2022 Contao Community Alliance. + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ namespace ContaoCommunityAlliance\DcGeneral\Controller; +use ContaoCommunityAlliance\DcGeneral\BaseConfigRegistryInterface; use ContaoCommunityAlliance\DcGeneral\Contao\DataDefinition\Definition\Contao2BackendViewDefinitionInterface; use ContaoCommunityAlliance\DcGeneral\Data\CollectionInterface; use ContaoCommunityAlliance\DcGeneral\Data\ConfigInterface; @@ -30,6 +31,7 @@ use ContaoCommunityAlliance\DcGeneral\Data\ModelId; use ContaoCommunityAlliance\DcGeneral\Data\ModelIdInterface; use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\BasicDefinitionInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\ModelRelationshipDefinitionInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\ModelRelationship\RootConditionInterface; @@ -55,7 +57,7 @@ class ModelCollector /** * The mode the definition is in. * - * @var int + * @var int|null */ private $definitionMode; @@ -76,35 +78,35 @@ class ModelCollector /** * The root data provider. * - * @var DataProviderInterface + * @var DataProviderInterface|null */ private $rootProvider; /** * The root data provider name. * - * @var string + * @var string|null */ private $rootProviderName; /** * The parent data provider. * - * @var DataProviderInterface + * @var DataProviderInterface|null */ private $parentProvider; /** * The parent data provider name. * - * @var string + * @var string|null */ private $parentProviderName; /** * The default data provider name. * - * @var string + * @var string|null */ private $defaultProviderName; @@ -117,9 +119,14 @@ class ModelCollector */ public function __construct(EnvironmentInterface $environment) { - $this->environment = $environment; - $definition = $this->environment->getDataDefinition(); - $basicDefinition = $definition->getBasicDefinition(); + $this->environment = $environment; + + $definition = $this->environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $basicDefinition = $definition->getBasicDefinition(); + assert($basicDefinition instanceof BasicDefinitionInterface); + $this->definitionMode = $basicDefinition->getMode(); $this->relationships = $definition->getModelRelationshipDefinition(); $this->defaultProviderName = $basicDefinition->getDataProvider(); @@ -147,20 +154,20 @@ public function __construct(EnvironmentInterface $environment) /** * Fetch a certain model from its provider. * - * @param string|ModelIdInterface $modelId This is either the id of the model or a serialized id. + * @param ModelIdInterface|string $modelId This is either the id of the model or a serialized id. * @param string|null $providerName The name of the provider, if this is empty, the id will be * deserialized and the provider name will get extracted from there. * - * @return ModelInterface + * @return ModelInterface|null * * @throws \InvalidArgumentException When the model id is invalid. */ public function getModel($modelId, $providerName = null) { if (\is_string($modelId)) { - try { + if (null !== $providerName) { $modelId = ModelId::fromValues($providerName, $modelId); - } catch (\Exception $swallow) { + } else { $modelId = ModelId::fromSerialized($modelId); } } @@ -169,14 +176,19 @@ public function getModel($modelId, $providerName = null) throw new \InvalidArgumentException('Invalid model id passed: ' . \var_export($modelId, true)); } - $definition = $this->environment->getDataDefinition(); + $definition = $this->environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + $parentDefinition = $this->environment->getParentDataDefinition(); - $dataProvider = $this->environment->getDataProvider($modelId->getDataProviderName()); - $config = $dataProvider->getEmptyConfig(); + + $dataProvider = $this->environment->getDataProvider($modelId->getDataProviderName()); + assert($dataProvider instanceof DataProviderInterface); + + $config = $dataProvider->getEmptyConfig(); if ($definition->getName() === $modelId->getDataProviderName()) { $propertyDefinition = $definition->getPropertiesDefinition(); - } elseif ($parentDefinition->getName() === $modelId->getDataProviderName()) { + } elseif ($parentDefinition && $parentDefinition->getName() === $modelId->getDataProviderName()) { $propertyDefinition = $parentDefinition->getPropertiesDefinition(); } else { throw new \InvalidArgumentException('Invalid provider name ' . $modelId->getDataProviderName()); @@ -228,8 +240,12 @@ public function searchParentOfIn(ModelInterface $model, CollectionInterface $mod } $provider = $this->environment->getDataProvider($condition->getDestinationName()); - $config = $provider->getEmptyConfig()->setFilter($condition->getFilter($candidate)); - $result = $this->searchParentOfIn($model, $provider->fetchAll($config)); + assert($provider instanceof DataProviderInterface); + + $config = $provider->getEmptyConfig()->setFilter($condition->getFilter($candidate)); + $parentCollection = $provider->fetchAll($config); + assert($parentCollection instanceof CollectionInterface); + $result = $this->searchParentOfIn($model, $parentCollection); if (null !== $result) { return $result; } @@ -289,12 +305,18 @@ public function searchParentFromHierarchical(ModelInterface $model): ?ModelInter if ($this->rootProviderName !== $model->getProviderName()) { throw new DcGeneralInvalidArgumentException( 'Model originates from ' . $model->getProviderName() . - ' but is expected to be from ' . $this->rootProviderName . + ' but is expected to be from ' . ($this->rootProviderName ?? '') . ' can not determine parent.' ); } - $condition = $this->relationships->getChildCondition($this->parentProviderName, $this->rootProviderName); + $parentProviderName = $this->parentProviderName; + assert(\is_string($parentProviderName)); + + $rootProviderName = $this->rootProviderName; + assert(\is_string($rootProviderName)); + + $condition = $this->relationships->getChildCondition($parentProviderName, $rootProviderName); if (null === $condition) { throw new DcGeneralInvalidArgumentException( 'Invalid configuration. Child condition must be defined!' @@ -304,7 +326,9 @@ public function searchParentFromHierarchical(ModelInterface $model): ?ModelInter return $this->parentProvider->fetch($this->parentProvider->getEmptyConfig()->setFilter($inverseFilter)); } - foreach ($this->parentProvider->fetchAll($this->parentProvider->getEmptyConfig()) as $candidate) { + $parentCollection = $this->parentProvider->fetchAll($this->parentProvider->getEmptyConfig()); + assert($parentCollection instanceof CollectionInterface); + foreach ($parentCollection as $candidate) { if ($condition->matches($candidate, $model)) { return $candidate; } @@ -316,39 +340,46 @@ public function searchParentFromHierarchical(ModelInterface $model): ?ModelInter /** * Retrieve all siblings of a given model. * - * @param ModelInterface $model The model for which the siblings shall be retrieved from. - * @param string|null $sortingProperty The property name to use for sorting. - * @param ModelIdInterface $parentId The (optional) parent id to use. + * @param ModelInterface $model The model for which the siblings shall be retrieved from. + * @param string|null $sortingProperty The property name to use for sorting. + * @param ModelIdInterface|null $parentId The (optional) parent id to use. * * @return CollectionInterface * - * @throws DcGeneralRuntimeException When no parent model can be located. */ public function collectSiblingsOf( ModelInterface $model, $sortingProperty = null, ModelIdInterface $parentId = null ) { - $config = $this->environment->getBaseConfigRegistry()->getBaseConfig($parentId); + $registry = $this->environment->getBaseConfigRegistry(); + assert($registry instanceof BaseConfigRegistryInterface); + + $config = $registry->getBaseConfig($parentId); // Add the parent filter. $this->addParentFilter($model, $config); if (null !== $sortingProperty) { - $config->setSorting([(string) $sortingProperty => 'ASC']); + $config->setSorting([$sortingProperty => 'ASC']); } // Handle grouping. $definition = $this->environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + /** @var Contao2BackendViewDefinitionInterface $viewDefinition */ $viewDefinition = $definition->getDefinition(Contao2BackendViewDefinitionInterface::NAME); - if ($viewDefinition && ($viewDefinition instanceof Contao2BackendViewDefinitionInterface)) { + if ($viewDefinition instanceof Contao2BackendViewDefinitionInterface) { $listingConfig = $viewDefinition->getListingConfig(); - $sortingProperties = \array_keys((array) $listingConfig->getDefaultSortingFields()); + /** @psalm-suppress DeprecatedMethod */ + $sortingProperties = \array_keys($listingConfig->getDefaultSortingFields()); $sortingPropertyIndex = \array_search($sortingProperty, $sortingProperties); if (false !== $sortingPropertyIndex && $sortingPropertyIndex > 0) { $sortingProperties = \array_slice($sortingProperties, 0, $sortingPropertyIndex); - $filters = $config->getFilter(); + + $filters = $config->getFilter(); + assert(\is_array($filters)); foreach ($sortingProperties as $propertyName) { $filters[] = [ @@ -362,7 +393,12 @@ public function collectSiblingsOf( } } - return $this->environment->getDataProvider($model->getProviderName())->fetchAll($config); + $dataProvider = $this->environment->getDataProvider($model->getProviderName()); + assert($dataProvider instanceof DataProviderInterface); + + $siblingCollection = $dataProvider->fetchAll($config); + assert($siblingCollection instanceof CollectionInterface); + return $siblingCollection; } /** @@ -406,12 +442,15 @@ public function collectDirectChildrenOf(ModelInterface $model, $providerName = ' * @param string $providerName The name of the data provider to fetch children from. * @param bool $recursive Determine for recursive sampling. For models with included child models. * - * @return array + * @return list * * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ - private function internalCollectChildrenOf(ModelInterface $model, $providerName = '', $recursive = false) - { + private function internalCollectChildrenOf( + ModelInterface $model, + string $providerName = '', + bool $recursive = false + ) { if ('' === $providerName) { $providerName = $model->getProviderName(); } @@ -422,10 +461,13 @@ private function internalCollectChildrenOf(ModelInterface $model, $providerName $childIds = []; foreach ($this->relationships->getChildConditions($model->getProviderName()) as $condition) { $provider = $this->environment->getDataProvider($condition->getDestinationName()); - $config = $provider->getEmptyConfig(); + assert($provider instanceof DataProviderInterface); + + $config = $provider->getEmptyConfig(); $config->setFilter($condition->getFilter($model)); $result = $provider->fetchAll($config); + assert($result instanceof CollectionInterface); if (!$recursive && $result->length() === 0) { return []; } @@ -444,7 +486,7 @@ private function internalCollectChildrenOf(ModelInterface $model, $providerName } } - return \array_merge($ids, ...$childIds); + return \array_values(\array_merge($ids, ...$childIds)); } /** @@ -460,7 +502,13 @@ private function searchParentOfInParentedMode(ModelInterface $model) { $this->guardParentProviderDefined(); - $condition = $this->relationships->getChildCondition($this->parentProviderName, $this->defaultProviderName); + $defaultProviderName = $this->defaultProviderName; + assert(\is_string($defaultProviderName)); + + $parentProviderName = $this->parentProviderName; + assert(\is_string($parentProviderName)); + + $condition = $this->relationships->getChildCondition($parentProviderName, $defaultProviderName); if (null === $condition) { throw new DcGeneralInvalidArgumentException( @@ -468,11 +516,15 @@ private function searchParentOfInParentedMode(ModelInterface $model) ); } + $parentProvider = $this->parentProvider; + assert($parentProvider instanceof DataProviderInterface); + if (null !== ($inverseFilter = $condition->getInverseFilterFor($model))) { - return $this->parentProvider->fetch($this->parentProvider->getEmptyConfig()->setFilter($inverseFilter)); + return $parentProvider->fetch($parentProvider->getEmptyConfig()->setFilter($inverseFilter)); } - - foreach ($this->parentProvider->fetchAll($this->parentProvider->getEmptyConfig()) as $candidate) { + $parentCollection = $parentProvider->fetchAll($parentProvider->getEmptyConfig()); + assert($parentCollection instanceof CollectionInterface); + foreach ($parentCollection as $candidate) { if ($condition->matches($candidate, $model)) { return $candidate; } @@ -508,8 +560,10 @@ private function searchParentOfInHierarchical(ModelInterface $model) } $provider = $this->environment->getDataProvider($condition->getSourceName()); + assert($provider instanceof DataProviderInterface); + $config = $provider->getEmptyConfig()->setFilter($inverseFilter); - $parent = $this->environment->getDataProvider($condition->getSourceName())->fetch($config); + $parent = $provider->fetch($config); if (null !== $parent) { return $parent; @@ -517,9 +571,14 @@ private function searchParentOfInHierarchical(ModelInterface $model) } // Start from the root data provider and walk through the whole tree. // To speed up, some conditions have an inverse filter - we should use them! - $config = $this->rootProvider->getEmptyConfig()->setFilter($this->rootCondition->getFilterArray()); - - return $this->searchParentOfIn($model, $this->rootProvider->fetchAll($config)); + $rootProvider = $this->rootProvider; + assert($rootProvider instanceof DataProviderInterface); + $rootCondition = $this->rootCondition; + assert($rootCondition instanceof RootConditionInterface); + $config = $rootProvider->getEmptyConfig()->setFilter($rootCondition->getFilterArray()); + $parentCollection = $rootProvider->fetchAll($config); + assert($parentCollection instanceof CollectionInterface); + return $this->searchParentOfIn($model, $parentCollection); } /** @@ -542,7 +601,10 @@ private function addParentFilter(ModelInterface $model, $config) // Root model? if ($this->isRootModel($model)) { - $config->setFilter($this->rootCondition->getFilterArray()); + $rootCondition = $this->rootCondition; + assert($rootCondition instanceof RootConditionInterface); + + $config->setFilter($rootCondition->getFilterArray()); return; } @@ -626,7 +688,7 @@ private function guardModelOriginatesFromProvider(ModelInterface $model): void throw new DcGeneralInvalidArgumentException( 'Model originates from ' . $model->getProviderName() . - ' but is expected to be from ' . $this->defaultProviderName . + ' but is expected to be from ' . ($this->defaultProviderName ?? '') . ' can not determine parent.' ); } diff --git a/src/Controller/SortingManager.php b/src/Controller/SortingManager.php index f65ec70b..8763b3c0 100644 --- a/src/Controller/SortingManager.php +++ b/src/Controller/SortingManager.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,7 @@ * @author Christian Schiffler * @author David Molineus * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -32,35 +32,35 @@ class SortingManager /** * The collection containing the models to be inserted. * - * @var CollectionInterface + * @var CollectionInterface|null */ protected $models; /** * The collection containing the models that are siblings. * - * @var CollectionInterface + * @var CollectionInterface|null */ - protected $siblings; + protected $siblings = null; /** * The collection containing the models that are siblings (working copy). * - * @var CollectionInterface + * @var CollectionInterface|null */ protected $siblingsCopy; /** * The result collection. * - * @var CollectionInterface + * @var CollectionInterface|null */ protected $results; /** * The model preceding the target position of the first model from the collection. * - * @var null|ModelInterface + * @var ModelInterface|null */ protected $previousModel; @@ -69,12 +69,12 @@ class SortingManager * * @var string */ - protected $sortingProperty; + protected $sortingProperty = ''; /** * Temporary marker containing the model currently in scope. * - * @var null|ModelInterface + * @var ModelInterface|null */ protected $marker; @@ -83,21 +83,21 @@ class SortingManager * * @var int */ - protected $position; + protected $position = 0; /** * Create a new instance. * - * @param CollectionInterface $models The collection containing the models to be inserted. - * @param CollectionInterface $siblings The collection containing the models that are siblings. - * @param string $sortedBy The property that is used for sorting. - * @param ModelInterface $previousModel The model preceding the target position of the first model from the - * collection. + * @param CollectionInterface|null $models The collection containing the models to be inserted. + * @param CollectionInterface|null $siblings The collection containing the models that are siblings. + * @param string|null $sortedBy The property that is used for sorting. + * @param ModelInterface|null $previousModel The model preceding the target position of the first model from + * the collection. */ public function __construct( CollectionInterface $models = null, CollectionInterface $siblings = null, - $sortedBy = null, + string $sortedBy = null, ModelInterface $previousModel = null ) { if ($models) { @@ -135,7 +135,7 @@ public function setModels(CollectionInterface $models) /** * Get the collection containing the models to be inserted. * - * @return CollectionInterface + * @return CollectionInterface|null */ public function getModels() { @@ -171,7 +171,7 @@ public function getPreviousModel() /** * Get the result collection. * - * @return CollectionInterface + * @return CollectionInterface|null */ public function getResults() { @@ -227,8 +227,11 @@ public function getSortingProperty() */ protected function getModelIds() { - $ids = []; + if (null === $this->models) { + return []; + } + $ids = []; foreach ($this->models as $model) { /** @var ModelInterface $model */ $ids[] = $model->getId(); @@ -248,32 +251,37 @@ protected function scanToDesiredPosition() $this->marker = null; $this->position = 0; $ids = $this->getModelIds(); + + $siblingsCopy = $this->siblingsCopy; + assert($siblingsCopy instanceof CollectionInterface); + // If no previous model, insert at beginning. if (null === $this->previousModel) { - if ($this->siblingsCopy->length()) { - $this->marker = $this->siblingsCopy->shift(); + if ($siblingsCopy->length()) { + $this->marker = $siblingsCopy->shift(); } return; } - if ($this->siblingsCopy->length()) { + $previousModel = $this->getPreviousModel(); + assert($previousModel instanceof ModelInterface); + + if ($siblingsCopy->length()) { // Search for "previous" sibling. do { - $this->marker = $this->siblingsCopy->shift(); - - if (\in_array($this->marker->getId(), $ids)) { - continue; - } - + $this->marker = $siblingsCopy->shift(); if ($this->marker) { + if (\in_array($this->marker->getId(), $ids, true)) { + continue; + } $this->position = $this->marker->getProperty($this->getSortingProperty()); } - } while ($this->marker && $this->marker->getId() !== $this->getPreviousModel()->getId()); + } while ($this->marker && $this->marker->getId() !== $previousModel->getId()); // Remember the "next" sibling. if ($this->marker) { - $this->marker = $this->siblingsCopy->shift(); + $this->marker = $siblingsCopy->shift(); } } } @@ -287,14 +295,20 @@ protected function scanToDesiredPosition() */ private function determineDelta() { + $marker = $this->marker; + assert($marker instanceof ModelInterface); + + $results = $this->results; + assert($results instanceof CollectionInterface); + $delta = ( - ($this->marker->getProperty($this->getSortingProperty()) - $this->position) / $this->results->length() + ($marker->getProperty($this->getSortingProperty()) - $this->position) / $results->length() ); // If delta too narrow, we need to make room. // Prevent delta to exceed, also. Use minimum delta which is calculated as multiple of 128. if (($delta < 2) || ($delta > 128)) { - return (\ceil($this->results->length() / 128) * 128); + return (\ceil($results->length() / 128) * 128); } return $delta; @@ -307,6 +321,8 @@ private function determineDelta() */ private function updateSorting() { + // Called from calculate() only when siblings exist. + assert($this->results instanceof CollectionInterface); $ids = $this->getModelIds(); // If no "next" sibling, simply increment the sorting as we are at the end of the list. if (!$this->marker) { @@ -323,7 +339,7 @@ private function updateSorting() // Loop over all models and increment sorting value. foreach ($this->results as $model) { - $this->position += $delta; + $this->position += (int) $delta; /** @var ModelInterface $model */ $model->setProperty($this->getSortingProperty(), $this->position); } @@ -332,17 +348,20 @@ private function updateSorting() // end of the list. if ($this->marker->getProperty($this->getSortingProperty()) <= $this->position) { do { + $siblingsCopy = $this->siblingsCopy; + assert($siblingsCopy instanceof CollectionInterface); + // Skip models about to be pasted. if (\in_array($this->marker->getId(), $ids)) { - $this->marker = $this->siblingsCopy->shift(); + $this->marker = $siblingsCopy->shift(); continue; } - $this->position += $delta; + $this->position += (int) $delta; $this->marker->setProperty($this->getSortingProperty(), $this->position); $this->results->push($this->marker); - $this->marker = $this->siblingsCopy->shift(); + $this->marker = $siblingsCopy->shift(); } while ($this->marker); } } @@ -356,16 +375,22 @@ private function updateSorting() */ protected function calculate() { - if (isset($this->results) || (0 === $this->models->length())) { + $models = $this->models; + assert($models instanceof CollectionInterface); + + if (isset($this->results) || (0 === $models->length())) { return; } if (!$this->getSortingProperty()) { - throw new \RuntimeException('No sorting property defined for ' . $this->models->get(0)->getProviderName()); + $firstModel = $models->get(0); + assert($firstModel instanceof ModelInterface); + + throw new \RuntimeException('No sorting property defined for ' . $firstModel->getProviderName()); } - $this->results = clone $this->models; - $this->siblingsCopy = clone $this->siblings; + $this->results = clone $models; + $this->siblingsCopy = $this->siblings ? clone $this->siblings : null; $this->scanToDesiredPosition(); $this->updateSorting(); diff --git a/src/Controller/TreeCollector.php b/src/Controller/TreeCollector.php index c0b34537..434026db 100644 --- a/src/Controller/TreeCollector.php +++ b/src/Controller/TreeCollector.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2020 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,18 +14,22 @@ * @author Christian Schiffler * @author Stefan Heimes * @author Sven Baumann - * @copyright 2013-2020 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ namespace ContaoCommunityAlliance\DcGeneral\Controller; +use ContaoCommunityAlliance\DcGeneral\BaseConfigRegistryInterface; use ContaoCommunityAlliance\DcGeneral\Data\CollectionInterface; use ContaoCommunityAlliance\DcGeneral\Data\ConfigInterface; use ContaoCommunityAlliance\DcGeneral\Data\DataProviderInterface; use ContaoCommunityAlliance\DcGeneral\Data\DCGE; use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\BasicDefinitionInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\ModelRelationshipDefinitionInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\ModelRelationship\FilterBuilder; use ContaoCommunityAlliance\DcGeneral\DataDefinition\ModelRelationship\ParentChildConditionInterface; @@ -35,6 +39,8 @@ /** * Generic class to retrieve a tree collection for tree views. + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class TreeCollector implements EnvironmentAwareInterface { @@ -82,7 +88,7 @@ public function __construct( ) { $this->environment = $environment; $this->panel = $panel; - $this->sorting = (array) $sorting; + $this->sorting = $sorting; $this->states = $states; } @@ -144,7 +150,10 @@ private function determineModelState(ModelInterface $model, $level) private function getChildProvidersOf($parentProvider, $relationships = null) { if (null === $relationships) { - $relationships = $this->getEnvironment()->getDataDefinition()->getModelRelationshipDefinition(); + $definition = $this->getEnvironment()->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $relationships = $definition->getModelRelationshipDefinition(); } $mySubTables = []; @@ -164,8 +173,11 @@ private function getChildProvidersOf($parentProvider, $relationships = null) * * @return CollectionInterface|null */ - private function getChildrenOfModel($dataProvider, $model, $childCondition) - { + private function getChildrenOfModel( + DataProviderInterface $dataProvider, + ModelInterface $model, + ParentChildConditionInterface $childCondition + ): ?CollectionInterface { $childIds = $dataProvider ->fetchAll( $dataProvider @@ -173,15 +185,13 @@ private function getChildrenOfModel($dataProvider, $model, $childCondition) ->setFilter($childCondition->getFilter($model)) ->setIdOnly(true) ); + assert(\is_array($childIds)); - if ( - ($childIds instanceof CollectionInterface && !$childIds->count()) - || (\is_array($childIds) && !\count($childIds)) - ) { + if (!\count($childIds)) { return null; } - return $dataProvider->fetchAll( + $children = $dataProvider->fetchAll( $dataProvider ->getEmptyConfig() ->setSorting(['sorting' => 'ASC']) @@ -192,6 +202,10 @@ private function getChildrenOfModel($dataProvider, $model, $childCondition) ->getAllAsArray() ) ); + + assert($children instanceof CollectionInterface); + + return $children; } /** @@ -203,10 +217,14 @@ private function getChildrenOfModel($dataProvider, $model, $childCondition) * * @return void */ - private function treeWalkModel(ModelInterface $model, $intLevel, $subTables = []) + private function treeWalkModel(ModelInterface $model, int $intLevel, array $subTables = []): void { - $environment = $this->getEnvironment(); - $relationships = $environment->getDataDefinition()->getModelRelationshipDefinition(); + $environment = $this->getEnvironment(); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $relationships = $definition->getModelRelationshipDefinition(); $hasChildren = false; $this->determineModelState($model, $intLevel); @@ -223,18 +241,19 @@ private function treeWalkModel(ModelInterface $model, $intLevel, $subTables = [] continue; } - $dataProvider = $environment->getDataProvider($subTable); - $childCollection = $this->getChildrenOfModel($dataProvider, $model, $childFilter); - $hasChildren = (bool) $childCollection; + $dataProvider = $environment->getDataProvider($subTable); + assert($dataProvider instanceof DataProviderInterface); - // Speed up - we may exit if we have at least one child but the parenting model is collapsed. - if ($hasChildren && !$model->getMeta($model::SHOW_CHILDREN)) { - break; - } + $childCollection = $this->getChildrenOfModel($dataProvider, $model, $childFilter); + $hasChildren = null !== $childCollection; if ($hasChildren) { + // Speed up - we may exit if we have at least one child but the parenting model is collapsed. + if (!$model->getMeta($model::SHOW_CHILDREN)) { + break; + } foreach ($childCollection as $childModel) { - // Let the child know about it's parent. + // Let the child know about its parent. $model->setMeta(ModelInterface::PARENT_ID, $model->getID()); $model->setMeta(ModelInterface::PARENT_PROVIDER_NAME, $providerName); @@ -264,29 +283,35 @@ private function treeWalkModel(ModelInterface $model, $intLevel, $subTables = [] */ private function addParentFilter(ConfigInterface $config, ModelInterface $parentModel) { - $environment = $this->getEnvironment(); - $definition = $environment->getDataDefinition(); + $environment = $this->getEnvironment(); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + $basicDefinition = $definition->getBasicDefinition(); + assert($basicDefinition instanceof BasicDefinitionInterface); - if (!$basicDefinition->getParentDataProvider()) { - return; - } + $parentDataProvider = $basicDefinition->getParentDataProvider(); + assert(\is_string($parentDataProvider)); - if ($basicDefinition->getParentDataProvider() !== $parentModel->getProviderName()) { + if ($parentDataProvider !== $parentModel->getProviderName()) { throw new \RuntimeException( \sprintf( 'Parent provider mismatch: %s vs. %s', - $basicDefinition->getParentDataProvider(), + $parentDataProvider, $parentModel->getProviderName() ) ); } + $rootDataProvider = $basicDefinition->getRootDataProvider(); + assert(\is_string($rootDataProvider)); + // Apply parent filtering, do this only for root elements. if ( $parentCondition = $definition->getModelRelationshipDefinition()->getChildCondition( - $basicDefinition->getParentDataProvider(), - $basicDefinition->getRootDataProvider() + $parentDataProvider, + $rootDataProvider ) ) { $baseFilter = $config->getFilter(); @@ -307,9 +332,10 @@ private function addParentFilter(ConfigInterface $config, ModelInterface $parent */ private function calculateRootConfig() { - $rootConfig = $this - ->getEnvironment() - ->getBaseConfigRegistry() + $registry = $this->getEnvironment()->getBaseConfigRegistry(); + assert($registry instanceof BaseConfigRegistryInterface); + + $rootConfig = $registry ->getBaseConfig() ->setSorting($this->getSorting()); $this->getPanel()->initialize($rootConfig); @@ -330,9 +356,11 @@ public function getTreeCollectionRecursive($rootId, $level = 0, $providerName = { $environment = $this->getEnvironment(); $dataProvider = $environment->getDataProvider($providerName); + assert($dataProvider instanceof DataProviderInterface); // Fetch root element. $rootModel = $dataProvider->fetch($this->calculateRootConfig()->setId($rootId)); + assert($rootModel instanceof ModelInterface); $this->treeWalkModel($rootModel, $level, $this->getChildProvidersOf($rootModel->getProviderName())); $rootCollection = $dataProvider->getEmptyCollection(); @@ -352,11 +380,17 @@ public function getTreeCollectionRecursive($rootId, $level = 0, $providerName = */ public function getChildrenOf($providerName, $level = 0, $parentModel = null) { - $environment = $this->getEnvironment(); + $environment = $this->getEnvironment(); + $dataProvider = $environment->getDataProvider($providerName); - $rootConfig = $this->calculateRootConfig(); + assert($dataProvider instanceof DataProviderInterface); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $rootConfig = $this->calculateRootConfig(); - $rootCondition = $environment->getDataDefinition()->getModelRelationshipDefinition()->getRootCondition(); + $rootCondition = $definition->getModelRelationshipDefinition()->getRootCondition(); if (null !== $rootCondition) { $baseFilter = $rootConfig->getFilter(); $filter = $rootCondition->getFilterArray(); @@ -374,6 +408,7 @@ public function getChildrenOf($providerName, $level = 0, $parentModel = null) $rootCollection = $dataProvider->fetchAll($rootConfig); $tableTreeData = $dataProvider->getEmptyCollection(); + assert($rootCollection instanceof CollectionInterface); if ($rootCollection->length() > 0) { $mySubTables = $this->getChildProvidersOf($providerName); diff --git a/src/Controller/TreeNodeStates.php b/src/Controller/TreeNodeStates.php index 9eadadfd..04501d2a 100644 --- a/src/Controller/TreeNodeStates.php +++ b/src/Controller/TreeNodeStates.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,8 @@ * @package contao-community-alliance/dc-general * @author Christian Schiffler * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -32,14 +33,14 @@ class TreeNodeStates * * @var array */ - private $states; + private $states = []; /** * List of implicit open nodes (selected values i.e.). * * @var array */ - private $implicitOpen; + private $implicitOpen = []; /** * Create a new instance. @@ -67,7 +68,7 @@ public function __construct($states = [], $implicitOpen = []) */ public function setStates($states) { - $this->states = (array) $states; + $this->states = $states; return $this; } @@ -96,7 +97,7 @@ public function getStates() */ public function setImplicitOpen($implicitOpen) { - $this->implicitOpen = (array) $implicitOpen; + $this->implicitOpen = $implicitOpen; return $this; } @@ -136,7 +137,7 @@ public function isAllOpen() */ public function setAllOpen($allOpen) { - $this->states['all'] = (bool) $allOpen; + $this->states['all'] = $allOpen; return $this; } @@ -205,7 +206,7 @@ public function setModelState($providerName, $modelId, $state) $this->states[$providerName] = []; } - $this->states[$providerName][$modelId] = (bool) $state; + $this->states[$providerName][$modelId] = $state; return $this; } diff --git a/src/DC/General.php b/src/DC/General.php index d91cacd6..d2f2f504 100644 --- a/src/DC/General.php +++ b/src/DC/General.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -20,7 +20,8 @@ * @author Simon Kusterer * @author David Molineus * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -30,14 +31,17 @@ use Contao\DataContainer; use Contao\System; use ContaoCommunityAlliance\DcGeneral\Action; +use ContaoCommunityAlliance\DcGeneral\Clipboard\ClipboardInterface; use ContaoCommunityAlliance\DcGeneral\Contao\Callback\Callbacks; use ContaoCommunityAlliance\DcGeneral\Controller\ControllerInterface; use ContaoCommunityAlliance\DcGeneral\Data\ModelId; use ContaoCommunityAlliance\DcGeneral\DataContainerInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralRuntimeException; use ContaoCommunityAlliance\DcGeneral\Factory\DcGeneralFactory; use ContaoCommunityAlliance\DcGeneral\Factory\Event\PopulateEnvironmentEvent; +use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; use ContaoCommunityAlliance\DcGeneral\View\ViewInterface; use ContaoCommunityAlliance\Translator\TranslatorInterface; use Symfony\Component\EventDispatcher\EventDispatcherInterface; @@ -82,8 +86,11 @@ public function __construct($tableName, array $module = [], CacheInterface $cach $dispatcher = $this->getEventDispatcher(); $fetcher = \Closure::bind(function (PopulateEnvironmentEvent $event) use ($tableNameCallback) { + $definition = $event->getEnvironment()->getDataDefinition(); + assert($definition instanceof ContainerInterface); + // We need to capture the correct environment and save it for later use. - if ($tableNameCallback !== $event->getEnvironment()->getDataDefinition()->getName()) { + if ($tableNameCallback !== $definition->getName()) { return; } $this->objEnvironment = $event->getEnvironment(); @@ -97,11 +104,11 @@ public function __construct($tableName, array $module = [], CacheInterface $cach ->createDcGeneral(); $dispatcher->removeListener(PopulateEnvironmentEvent::NAME, $fetcher); + $clipboard = $this->getEnvironment()->getClipboard(); + assert($clipboard instanceof ClipboardInterface); + // Load the clipboard. - $this - ->getEnvironment() - ->getClipboard() - ->loadFrom($this->getEnvironment()); + $clipboard->loadFrom($this->getEnvironment()); // Execute AJAX request, called from Backend::getBackendModule // we have to do this here, as otherwise the script will exit as it only checks for DC_Table and DC_File @@ -187,6 +194,7 @@ public function __get($name) { $environment = $this->getEnvironment(); $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); switch ($name) { case 'id': @@ -200,7 +208,10 @@ public function __get($name) return ModelId::fromSerialized($inputProvider->getParameter($idParameter))->getId(); case 'table': - return $environment->getDataDefinition()->getName(); + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + return $definition->getName(); default: } @@ -214,7 +225,10 @@ public function __get($name) */ public function getName() { - return $this->getEnvironment()->getDataDefinition()->getName(); + $definition = $this->getEnvironment()->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + return $definition->getName(); } /** @@ -263,7 +277,10 @@ public function getControllerHandler() */ public function __call($name, $arguments) { - return $this->getEnvironment()->getController()->handle(new Action($name, $arguments)); + $controller = $this->getEnvironment()->getController(); + assert($controller instanceof ControllerInterface); + + return $controller->handle(new Action($name, $arguments)); } /** @@ -274,9 +291,16 @@ public function __call($name, $arguments) protected function callAction() { $environment = $this->getEnvironment(); - $action = new Action($environment->getInputProvider()->getParameter('act') ?: 'showAll'); - return $environment->getController()->handle($action); + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + + $controller = $environment->getController(); + assert($controller instanceof ControllerInterface); + + $action = new Action($inputProvider->getParameter('act') ?: 'showAll'); + + return $controller->handle($action); } /** diff --git a/src/Data/AbstractModel.php b/src/Data/AbstractModel.php index 3161d8f0..fb3ac23b 100644 --- a/src/Data/AbstractModel.php +++ b/src/Data/AbstractModel.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -24,7 +25,7 @@ /** * Class AbstractModel. * Abstract base class for data provider models. - * This class implements the setter and getter for meta data. + * This class implements the setter and getter for meta-data. */ abstract class AbstractModel implements ModelInterface { @@ -38,16 +39,16 @@ abstract class AbstractModel implements ModelInterface /** * {@inheritdoc} */ - public function getMeta($metaName) + public function getMeta($strMetaName) { - return ($this->arrMetaInformation[$metaName] ?? null); + return ($this->arrMetaInformation[$strMetaName] ?? null); } /** * {@inheritdoc} */ - public function setMeta($metaName, $value) + public function setMeta($strMetaName, $varValue) { - $this->arrMetaInformation[$metaName] = $value; + $this->arrMetaInformation[$strMetaName] = $varValue; } } diff --git a/src/Data/ConfigInterface.php b/src/Data/ConfigInterface.php index c42ab7d6..0f200413 100644 --- a/src/Data/ConfigInterface.php +++ b/src/Data/ConfigInterface.php @@ -154,7 +154,7 @@ public function setFilter($arrFilter); * * The returning array will be of 'property name' => 'ASC|DESC' nature. * - * @return array + * @return array */ public function getSorting(); @@ -163,7 +163,7 @@ public function getSorting(); * * The array must be of 'property name' => 'ASC|DESC' nature. * - * @param array $arrSorting The sorting array to use. + * @param array $arrSorting The sorting array to use. * * @return ConfigInterface */ @@ -197,7 +197,7 @@ public function get($strKey); /** * Set the additional information. * - * @param string $strKey The name of the information to retrieve. + * @param string $strKey The name of the information to retrieve. * * @param mixed $varValue The value to store. * diff --git a/src/Data/DataProviderInterface.php b/src/Data/DataProviderInterface.php index f402dc37..bf2d883c 100644 --- a/src/Data/DataProviderInterface.php +++ b/src/Data/DataProviderInterface.php @@ -86,7 +86,7 @@ public function fetch(ConfigInterface $config); * * @param ConfigInterface $config The configuration to use. * - * @return CollectionInterface|ModelInterface[]|string[] + * @return CollectionInterface|list */ public function fetchAll(ConfigInterface $config); @@ -170,7 +170,7 @@ public function saveVersion(ModelInterface $model, $username); * @param mixed $mixID The ID of the record. * @param mixed $mixVersion The ID of the version. * - * @return ModelInterface + * @return ModelInterface|null */ public function getVersion($mixID, $mixVersion); diff --git a/src/Data/DefaultCollection.php b/src/Data/DefaultCollection.php index ce332200..f43c9e41 100644 --- a/src/Data/DefaultCollection.php +++ b/src/Data/DefaultCollection.php @@ -65,7 +65,7 @@ class DefaultCollection implements CollectionInterface */ public function length(): int { - return count($this->arrCollection); + return \count($this->arrCollection); } /** @@ -81,7 +81,7 @@ public function count(): int */ public function offsetExists($offset): bool { - return array_key_exists($offset, $this->arrCollection); + return \array_key_exists($offset, $this->arrCollection); } /** @@ -97,6 +97,7 @@ public function offsetGet($offset): ?ModelInterface */ public function offsetSet($offset, $value): void { + assert(\is_int($offset)); $this->arrCollection[$offset] = $value; } @@ -113,7 +114,7 @@ public function offsetUnset($offset): void */ public function get($index): ?ModelInterface { - if (array_key_exists($index, $this->arrCollection)) { + if (\array_key_exists($index, $this->arrCollection)) { return $this->arrCollection[$index]; } @@ -133,8 +134,8 @@ public function push(ModelInterface $model): void */ public function pop(): ?ModelInterface { - if (count($this->arrCollection)) { - return array_pop($this->arrCollection); + if (\count($this->arrCollection)) { + return \array_pop($this->arrCollection); } return null; @@ -146,7 +147,7 @@ public function pop(): ?ModelInterface public function unshift(ModelInterface $model): void { if ($model->hasProperties()) { - array_unshift($this->arrCollection, $model); + \array_unshift($this->arrCollection, $model); } } @@ -155,8 +156,8 @@ public function unshift(ModelInterface $model): void */ public function shift(): ?ModelInterface { - if (count($this->arrCollection)) { - return array_shift($this->arrCollection); + if (\count($this->arrCollection)) { + return \array_shift($this->arrCollection); } return null; @@ -177,7 +178,7 @@ public function insert($index, ModelInterface $model): void */ public function remove($mixedValue): void { - if (is_object($mixedValue)) { + if (\is_object($mixedValue)) { foreach ($this->arrCollection as $collectionIndex => $model) { if ($mixedValue === $model) { unset($this->arrCollection[$collectionIndex]); @@ -187,7 +188,7 @@ public function remove($mixedValue): void unset($this->arrCollection[$mixedValue]); } - $this->arrCollection = array_values($this->arrCollection); + $this->arrCollection = \array_values($this->arrCollection); } /** diff --git a/src/Data/DefaultConfig.php b/src/Data/DefaultConfig.php index 6e21ec84..f88a4d20 100644 --- a/src/Data/DefaultConfig.php +++ b/src/Data/DefaultConfig.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -15,7 +15,8 @@ * @author Tristan Lins * @author Stefan Heimes * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -73,12 +74,12 @@ class DefaultConfig implements ConfigInterface * * @var array|null */ - protected $arrFilter; + protected $arrFilter = null; /** * The properties to use for sorting. * - * @var array(string => string) + * @var array */ protected $arrSorting = []; @@ -87,7 +88,7 @@ class DefaultConfig implements ConfigInterface * * @var array|null */ - protected $arrFields; + protected $arrFields = null; /** * Miscellaneous arbitrary data stored in the config. @@ -106,7 +107,6 @@ class DefaultConfig implements ConfigInterface */ private function __construct() { - return $this; } /** @@ -116,6 +116,7 @@ private function __construct() */ public static function init() { + /** @psalm-suppress UnsafeInstantiation */ return new static(); } @@ -132,13 +133,13 @@ public function getId() /** * Set a specific id for an element to be retrieved. * - * @param mixed $currentId The id of the element to be retrieved. + * @param mixed $mixId The id of the element to be retrieved. * * @return ConfigInterface */ - public function setId($currentId) + public function setId($mixId) { - $this->mixId = $currentId; + $this->mixId = $mixId; return $this; } @@ -147,16 +148,19 @@ public function setId($currentId) * Get list of specific ids to be retrieved. * * @return array + * + * @deprecated Use filters instead. */ public function getIds() { // @codingStandardsIgnoreStart @\trigger_error( 'The method setids in the DefaultConfig is deprecated since 2.1 and will be removed in 3.0.', - E_NOTICE + E_USER_NOTICE ); // @codingStandardsIgnoreEnd + /** @psalm-suppress DeprecatedProperty */ return $this->arrIds; } @@ -166,18 +170,21 @@ public function getIds() * @param array $arrIds The list of ids to be retrieved. * * @return ConfigInterface + * + * @deprecated Use filters instead. */ public function setIds($arrIds) { // @codingStandardsIgnoreStart @\trigger_error( - 'The method setids in the DefaultConfig is deprecated since 2.1 and will be removed in 3.0. - Use set filter + 'The method setids in the DefaultConfig is deprecated since 2.1 and will be removed in 3.0. + Use set filter $dataConfig->setFilter([[\'operation\' => \'IN\', \'property\' => \'id\', \'values\' => [4,3,2,1]]]).', - E_NOTICE + E_USER_NOTICE ); // @codingStandardsIgnoreEnd + /** @psalm-suppress DeprecatedProperty */ $this->arrIds = $arrIds; return $this; @@ -198,13 +205,13 @@ public function getIdOnly() /** * Set flag for return id only. * - * @param boolean $idOnly Boolean flag to determine that only Ids shall be returned when calling fetchAll(). + * @param boolean $blnIdOnly Boolean flag to determine that only Ids shall be returned when calling fetchAll(). * * @return DefaultConfig */ - public function setIdOnly($idOnly) + public function setIdOnly($blnIdOnly) { - $this->blnIdOnly = $idOnly; + $this->blnIdOnly = $blnIdOnly; return $this; } @@ -226,13 +233,13 @@ public function getStart() * * This is the offset to use for pagination. * - * @param integer $start Number of first element to return. + * @param integer $intStart Number of first element to return. * * @return ConfigInterface */ - public function setStart($start) + public function setStart($intStart) { - $this->intStart = $start; + $this->intStart = $intStart; return $this; } @@ -254,13 +261,13 @@ public function getAmount() * * This is the amount of items to return for pagination. * - * @param int $amount The amount to use. + * @param int $intAmount The amount to use. * * @return ConfigInterface */ - public function setAmount($amount) + public function setAmount($intAmount) { - $this->intAmount = $amount; + $this->intAmount = $intAmount; return $this; } @@ -278,13 +285,13 @@ public function getFilter() /** * Set the list with filter options. * - * @param array $filters The array containing the filter values. + * @param array $arrFilter The array containing the filter values. * * @return ConfigInterface */ - public function setFilter($filters) + public function setFilter($arrFilter) { - $this->arrFilter = $filters; + $this->arrFilter = $arrFilter; return $this; } @@ -294,7 +301,7 @@ public function setFilter($filters) * * The returning array will be of 'property name' => 'ASC|DESC' nature. * - * @return array + * @return array */ public function getSorting() { @@ -306,13 +313,13 @@ public function getSorting() * * The array must be of 'property name' => 'ASC|DESC' nature. * - * @param array $sortingProperties The sorting array to use. + * @param array $arrSorting The sorting array to use. * * @return ConfigInterface */ - public function setSorting($sortingProperties) + public function setSorting($arrSorting) { - $this->arrSorting = $sortingProperties; + $this->arrSorting = $arrSorting; return $this; } @@ -330,13 +337,13 @@ public function getFields() /** * Set the list of fields to be retrieved. * - * @param array $fields Array of property names. + * @param array $arrFields Array of property names. * * @return ConfigInterface */ - public function setFields($fields) + public function setFields($arrFields) { - $this->arrFields = $fields; + $this->arrFields = $arrFields; return $this; } @@ -344,14 +351,14 @@ public function setFields($fields) /** * Get the additional information. * - * @param string $informationName The name of the information to retrieve. + * @param string $strKey The name of the information to retrieve. * * @return mixed || null */ - public function get($informationName) + public function get($strKey) { - if (isset($this->arrData[$informationName])) { - return $this->arrData[$informationName]; + if (isset($this->arrData[$strKey])) { + return $this->arrData[$strKey]; } return null; @@ -360,14 +367,14 @@ public function get($informationName) /** * Set the additional information. * - * @param string $informationName The name of the information to retrieve. - * @param mixed $value The value to store. + * @param string $strKey The name of the information to retrieve. + * @param mixed $varValue The value to store. * * @return ConfigInterface */ - public function set($informationName, $value) + public function set($strKey, $varValue) { - $this->arrData[$informationName] = $value; + $this->arrData[$strKey] = $varValue; return $this; } diff --git a/src/Data/DefaultDataProvider.php b/src/Data/DefaultDataProvider.php index 9d89582a..a8ff5f22 100644 --- a/src/Data/DefaultDataProvider.php +++ b/src/Data/DefaultDataProvider.php @@ -24,6 +24,7 @@ * @author Ingolf Steinhardt * @author Richard Henkenjohann * @author Alex Wuttke + * @author Ingolf Steinhardt * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource @@ -37,6 +38,7 @@ use Contao\System; use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralRuntimeException; use Doctrine\DBAL\Connection; +use Doctrine\DBAL\Exception; use Doctrine\DBAL\Schema\Table; /** @@ -48,6 +50,9 @@ * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) - There is no elegant way to reduce this class more without * reducing the interface. * @SuppressWarnings(PHPMD.CouplingBetweenObjects) - Can not reduce without changing the interface. + * @SuppressWarnings(PHPMD.ExcessiveClassLength) - Can not reduce without changing the interface. + * + * @psalm-suppress MissingConstructor - properties will get set in setBaseConfig(). */ class DefaultDataProvider implements DataProviderInterface { @@ -56,7 +61,7 @@ class DefaultDataProvider implements DataProviderInterface * * @var string */ - protected $source; + protected $source = ''; /** * The Database instance. @@ -75,23 +80,23 @@ class DefaultDataProvider implements DataProviderInterface /** * The property that shall get populated with the current timestamp when saving data. * - * @var string + * @var string|null */ - protected $timeStampProperty = false; + protected $timeStampProperty = null; /** * The id generator to use (if any). * - * @var IdGeneratorInterface + * @var ?IdGeneratorInterface */ - protected $idGenerator; + protected $idGenerator = null; /** * The table schema. * - * @var Table + * @var ?Table */ - private $schema; + private ?Table $schema = null; /** * Retrieve the name of the id property. @@ -130,7 +135,7 @@ public function getTimeStampProperty() /** * Set the property name that shall get updated with the current time stamp when saving to the database. * - * @param boolean $timeStampField The property name or empty to clear. + * @param string $timeStampField The property name or empty to clear. * * @return DefaultDataProvider */ @@ -158,7 +163,7 @@ public function setIdGenerator($idGenerator) /** * Retrieve the id generator. * - * @return IdGeneratorInterface + * @return ?IdGeneratorInterface */ public function getIdGenerator() { @@ -199,6 +204,7 @@ public function setBaseConfig(array $config) throw new DcGeneralRuntimeException('Missing table name.'); } + /** @psalm-suppress DeprecatedMethod - bc layer. */ $this->fallbackFromDatabaseToConnection($config); if (isset($config['connection'])) { @@ -326,6 +332,7 @@ public function delete($item) protected function createModelFromDatabaseResult(array $result) { $model = $this->getEmptyModel(); + assert($model instanceof DefaultModel); foreach ($result as $key => $value) { if ($key === $this->idProperty) { @@ -368,6 +375,9 @@ public function fetch(ConfigInterface $config) } $result = $statement->fetchAssociative(); + if (false === $result) { + return null; + } return $this->createModelFromDatabaseResult($result); } @@ -387,13 +397,16 @@ public function fetchAll(ConfigInterface $config) if (0 !== $config->getAmount()) { $queryBuilder->setMaxResults($config->getAmount()); - $queryBuilder->setFirstResult($config->getStart() ?? 0); + $queryBuilder->setFirstResult($config->getStart()); } $collection = $this->getEmptyCollection(); $statement = $queryBuilder->executeQuery(); if (0 === $statement->rowCount()) { + if ($config->getIdOnly()) { + return []; + } return $collection; } @@ -420,7 +433,7 @@ public function getFilterOptions(ConfigInterface $config) { $internalConfig = $this->prefixDataProviderProperties($config); $properties = $internalConfig->getFields(); - if (1 !== \count($properties)) { + if (null === $properties || 1 !== \count($properties)) { throw new DcGeneralRuntimeException('objConfig must contain exactly one property to be retrieved.'); } $property = $properties[0]; @@ -436,7 +449,7 @@ public function getFilterOptions(ConfigInterface $config) $values = $statement->fetchAllAssociative(); $filterProperties = $config->getFields(); - if (1 !== \count($filterProperties)) { + if (null === $filterProperties || 1 !== \count($filterProperties)) { throw new DcGeneralRuntimeException('objConfig must contain exactly one property to be retrieved.'); } $filterProperty = $filterProperties[0]; @@ -462,9 +475,7 @@ public function getCount(ConfigInterface $config) $queryBuilder->from($this->source); DefaultDataProviderDBalUtils::addWhere($internalConfig, $queryBuilder); - $statement = $queryBuilder->executeQuery(); - - return $statement->fetchFirstColumn(); + return (int) $queryBuilder->executeQuery()->fetchOne(); } /** @@ -480,17 +491,14 @@ public function isUniqueValue($field, $new, $primaryId = null) $queryBuilder->setParameter($field, $new); $statement = $queryBuilder->executeQuery(); - $unique = $statement->fetchAssociative(); - - if (0 === $statement->rowCount()) { + $count = $statement->rowCount(); + if (0 === $count) { return true; } + $unique = $statement->fetchAssociative(); + assert(\is_array($unique)); - if (($primaryId === $unique['id']) && (1 === $statement->rowCount())) { - return true; - } - - return false; + return ($primaryId === $unique['id']) && (1 === $count); } /** @@ -505,7 +513,7 @@ public function resetFallback($field) ); // @codingStandardsIgnoreEnd - $this->connection->query('UPDATE ' . $this->source . ' SET ' . $field . ' = \'\'')->execute(); + $this->connection->executeQuery('UPDATE ' . $this->source . ' SET ' . $field . ' = \'\''); } /** @@ -627,7 +635,7 @@ private function convertModelToDataPropertyArray(ModelInterface $model, int $tim } if ($this->timeStampProperty) { - $data[$this->source . '.' . $this->getTimeStampProperty()] = $timestamp ?: \time(); + $data[$this->source . '.' . ($this->getTimeStampProperty() ?? '')] = $timestamp ?: \time(); } return $data; @@ -644,8 +652,8 @@ private function convertModelToDataPropertyArray(ModelInterface $model, int $tim private function insertModelIntoDatabase(ModelInterface $model, $timestamp = 0): void { $data = $this->convertModelToDataPropertyArray($model, $timestamp); - if ($this->getIdGenerator()) { - $model->setId($this->getIdGenerator()->generate()); + if ($generator = $this->getIdGenerator()) { + $model->setId($generator->generate()); $data[$this->idProperty] = $model->getId(); } @@ -711,8 +719,8 @@ public function saveEach(CollectionInterface $items, $timestamp = 0) */ public function fieldExists($columnName) { - if (!isset($this->schema)) { - $this->schema = $this->connection->getSchemaManager()->listTableDetails($this->source); + if (null === $this->schema) { + $this->schema = $this->connection->createSchemaManager()->introspectTable($this->source); } return $this->schema->hasColumn($columnName); @@ -740,7 +748,8 @@ public function getVersion($mixID, $mixVersion) return null; } - if (null === $version = $statement->fetchAllAssociative()) { + /** @var array{data: string}|false $version */ + if (false === $version = $statement->fetchAssociative()) { return null; } @@ -793,14 +802,14 @@ public function getVersions($mixID, $onlyActive = false) $statement = $queryBuilder->executeQuery(); if (0 === $statement->rowCount()) { - return null; + return $this->getEmptyCollection(); } $versions = $statement->fetchAllAssociative(); $collection = $this->getEmptyCollection(); - foreach ((array) $versions as $versionValue) { + foreach ($versions as $versionValue) { $model = $this->getEmptyModel(); $model->setId($mixID); @@ -839,7 +848,7 @@ public function saveVersion(ModelInterface $model, $username) $queryBuilder->setParameter('fromTable', $this->source); $statement = $queryBuilder->executeQuery(); - $count = $statement->fetchFirstColumn(); + $count = $statement->fetchOne(); $mixNewVersion = ((int) $count + 1); $mixData = $model->getPropertiesAsArray(); @@ -867,6 +876,8 @@ public function saveVersion(ModelInterface $model, $username) * @param mixed $mixVersion The version number to set active. * * @return void + * + * @throws Exception */ public function setVersionActive($mixID, $mixVersion) { @@ -906,7 +917,13 @@ public function getActiveVersion($mixID) return null; } - return $statement->fetchAllAssociative()['version']; + /** @var array{version: int}|false $row */ + $row = $statement->fetchAssociative(); + if (false === $row) { + return null; + } + + return $row['version']; } /** @@ -963,6 +980,9 @@ protected function insertUndo($sourceSQL, $saveSQL, $table) } $result = $statement->fetchAssociative(); + if (false === $result) { + return; + } // Save information in array. $parameters = []; @@ -994,7 +1014,10 @@ protected function insertUndo($sourceSQL, $saveSQL, $table) */ protected function getDefaultConnection() { - return System::getContainer()->get('database_connection'); + $connection = System::getContainer()->get('database_connection'); + assert($connection instanceof Connection); + + return $connection; } /** diff --git a/src/Data/DefaultDataProviderDBalUtils.php b/src/Data/DefaultDataProviderDBalUtils.php index 7abf47e2..0200fffa 100644 --- a/src/Data/DefaultDataProviderDBalUtils.php +++ b/src/Data/DefaultDataProviderDBalUtils.php @@ -26,6 +26,15 @@ /** * Static helper class for DefaultDataProvider. + * + * NOTE: Can't define filters recursively. See: https://github.com/vimeo/psalm/issues/2777 + * @psalm-type TSQLFilterAND=array{operation: 'AND', children: list} + * @psalm-type TSQLFilterOR=array{operation: 'OR', children: list} + * @psalm-type TSQLFilterCMP=array{operation: "="|">"|">="|"<"|"<="|"<>", property: string, value: string|int} + * @psalm-type TSQLFilterIN=array{operation: 'IN'|'NOT IN', property: string, values: list} + * @psalm-type TSQLFilterLIKE=array{operation: 'LIKE'|'NOT LIKE', property: string, value: string} + * @psalm-type TSQLFilterNULL=array{operation: 'IS NULL'|'IS NOT NULL', property: string, value: string} + * @psalm-type TSQLFilter=TSQLFilterAND|TSQLFilterOR|TSQLFilterCMP|TSQLFilterIN|TSQLFilterLIKE|TSQLFilterNULL */ class DefaultDataProviderDBalUtils { @@ -47,19 +56,20 @@ public static function addField(ConfigInterface $config, $idProperty, QueryBuild return; } - if (null !== $config->getFields()) { + if (null !== $fieldList = $config->getFields()) { + /** @psalm-suppress DeprecatedMethod - bc layer */ $connection = $queryBuilder->getConnection(); - $fields = implode( + $fields = \implode( ', ', - array_map( + \array_map( function ($field) use ($connection) { return $connection->quoteIdentifier($field); }, - $config->getFields() + $fieldList ) ); - if (false !== stripos($fields, 'DISTINCT') || \in_array($idProperty, $config->getFields(), true)) { + if (false !== \stripos($fields, 'DISTINCT') || \in_array($idProperty, $fieldList, true)) { $queryBuilder->select($fields); return; } @@ -78,8 +88,6 @@ function ($field) use ($connection) { * @param QueryBuilder $queryBuilder The query builder. * * @return void - * - * @internal param array $parameters The query parameters will get stored into this array. */ public static function addWhere($config, QueryBuilder $queryBuilder) { @@ -96,19 +104,20 @@ public static function addWhere($config, QueryBuilder $queryBuilder) */ public static function addSorting(ConfigInterface $config, QueryBuilder $queryBuilder) { - if (empty($config->getSorting()) || !is_array($config->getSorting())) { + $sorting = $config->getSorting(); + if (empty($sorting)) { return; } - foreach ($config->getSorting() as $sort => $order) { + foreach ($sorting as $sort => $order) { if (empty($sort)) { continue; } // array could be a simple field list or list of field => direction combinations. if (!empty($order)) { - $order = strtoupper($order); - if (!in_array($order, [DCGE::MODEL_SORTING_ASC, DCGE::MODEL_SORTING_DESC])) { + $order = \strtoupper($order); + if (!\in_array($order, [DCGE::MODEL_SORTING_ASC, DCGE::MODEL_SORTING_DESC])) { $sort = $order; $order = DCGE::MODEL_SORTING_ASC; } @@ -127,18 +136,17 @@ public static function addSorting(ConfigInterface $config, QueryBuilder $queryBu * @param QueryBuilder $queryBuilder The query builder. * * @return string The combined conditions. - * - * @internal param array $parameters The query parameters will get stored into this array. */ - private static function addFilter($config, QueryBuilder $queryBuilder) + private static function addFilter(ConfigInterface $config, QueryBuilder $queryBuilder): string { - $result = static::calculateSubFilter( - ['operation' => 'AND', 'children' => $config->getFilter()], - $queryBuilder - ); + /** @var list $filter */ + $filter = $config->getFilter() ?? []; + + /** @var TSQLFilterAND $andFilter */ + $andFilter = ['operation' => 'AND', 'children' => $filter]; // Combine filter syntax. - return $result ?: ''; + return static::calculateSubFilter($andFilter, $queryBuilder); } /** @@ -167,22 +175,16 @@ private static function addFilter($config, QueryBuilder $queryBuilder) * 'property' string (the name of a property) * 'value' literal - Wildcards * (Many) ? (One) * - * @param array $filter The filter to be combined to a valid SQL filter query. + * @param TSQLFilter $filter The filter to be combined to a valid SQL filter query. * @param QueryBuilder $queryBuilder The query builder. * * @return string The combined WHERE conditions. * * @throws DcGeneralRuntimeException If the sub filter has a sub filter. * @throws DcGeneralRuntimeException If the sub filter has a no filter. - * - * @internal param array $parameters The query parameters will get stored into this array. */ - private static function calculateSubFilter($filter, QueryBuilder $queryBuilder) + private static function calculateSubFilter(array $filter, QueryBuilder $queryBuilder): string { - if (!is_array($filter)) { - throw new DcGeneralRuntimeException('Error Processing sub filter: ' . var_export($filter, true), 1); - } - if (null !== ($rule = static::determineFilterAndOr($filter, $queryBuilder))) { return $rule; } @@ -209,16 +211,17 @@ private static function calculateSubFilter($filter, QueryBuilder $queryBuilder) /** * Determine sql filter for and/or. * - * @param array $filter The query filter. + * @param TSQLFilter $filter The query filter. * @param QueryBuilder $queryBuilder The query builder. * * @return string|null */ - private static function determineFilterAndOr(array $filter, QueryBuilder $queryBuilder) + private static function determineFilterAndOr(array $filter, QueryBuilder $queryBuilder): ?string { - if (!\in_array($filter['operation'], ['AND', 'OR'])) { + if (!\in_array($filter['operation'], ['AND', 'OR'], true)) { return null; } + /** @var TSQLFilterAND|TSQLFilterOR $filter */ static::filterAndOr($filter, $queryBuilder); @@ -228,16 +231,17 @@ private static function determineFilterAndOr(array $filter, QueryBuilder $queryB /** * Determine sql filter for comparing. * - * @param array $filter The query filter. + * @param TSQLFilter $filter The query filter. * @param QueryBuilder $queryBuilder The query builder. * * @return string|null */ - private static function determineFilterComparing(array $filter, QueryBuilder $queryBuilder) + private static function determineFilterComparing(array $filter, QueryBuilder $queryBuilder): ?string { - if (!\in_array($filter['operation'], ['=', '>', '>=', '<', '<=', '<>'])) { + if (!\in_array($filter['operation'], ['=', '>', '>=', '<', '<=', '<>'], true)) { return null; } + /** @var TSQLFilterCMP $filter */ return static::filterComparing($filter, $queryBuilder); } @@ -245,16 +249,17 @@ private static function determineFilterComparing(array $filter, QueryBuilder $qu /** * Determine sql filter for in or not in list. * - * @param array $filter The query filter. + * @param TSQLFilter $filter The query filter. * @param QueryBuilder $queryBuilder The query builder. * * @return string|null */ - private static function determineFilterInOrNotInList(array $filter, QueryBuilder $queryBuilder) + private static function determineFilterInOrNotInList(array $filter, QueryBuilder $queryBuilder): ?string { if (!\in_array($filter['operation'], ['IN', 'NOT IN'])) { return null; } + /** @var TSQLFilterIN $filter */ return static::filterInOrNotInList($filter, $queryBuilder); } @@ -262,7 +267,7 @@ private static function determineFilterInOrNotInList(array $filter, QueryBuilder /** * Determine sql filter for like or not like. * - * @param array $filter The query filter. + * @param TSQLFilter $filter The query filter. * @param QueryBuilder $queryBuilder The query builder. * * @return string|null @@ -272,6 +277,7 @@ private static function determineFilterLikeOrNotLike(array $filter, QueryBuilder if (!\in_array($filter['operation'], ['LIKE', 'NOT LIKE'])) { return null; } + /** @var TSQLFilterLIKE $filter */ return static::filterLikeOrNotLike($filter, $queryBuilder); } @@ -279,7 +285,7 @@ private static function determineFilterLikeOrNotLike(array $filter, QueryBuilder /** * Determine sql filter for is null or is not null. * - * @param array $filter The query filter. + * @param TSQLFilter $filter The query filter. * @param QueryBuilder $queryBuilder The query builder. * * @return string|null @@ -289,6 +295,7 @@ private static function determineFilterIsNullOrIsNotNull(array $filter, QueryBui if (!\in_array($filter['operation'], ['IS NULL', 'IS NOT NULL'])) { return null; } + /** @var TSQLFilterNULL $filter */ return static::filterIsNullOrIsNotNull($filter, $queryBuilder); } @@ -296,14 +303,12 @@ private static function determineFilterIsNullOrIsNotNull(array $filter, QueryBui /** * Build an AND or OR query. * - * @param array $operation The operation to convert. - * @param QueryBuilder $queryBuilder The query builder. + * @param TSQLFilterAND|TSQLFilterOR $operation The operation to convert. + * @param QueryBuilder $queryBuilder The query builder. * * @return void - * - * @internal param array $params The parameter array for the resulting query. */ - private static function filterAndOr($operation, QueryBuilder $queryBuilder) + private static function filterAndOr(array $operation, QueryBuilder $queryBuilder): void { $children = $operation['children']; @@ -311,8 +316,9 @@ private static function filterAndOr($operation, QueryBuilder $queryBuilder) return; } - $whereOperation = strtolower($operation['operation']) . 'Where'; + $whereOperation = \strtolower($operation['operation']) . 'Where'; + /** @var TSQLFilter $child */ foreach ($children as $child) { if ('' !== $child = static::calculateSubFilter($child, $queryBuilder)) { $queryBuilder->{$whereOperation}($child); @@ -323,14 +329,12 @@ private static function filterAndOr($operation, QueryBuilder $queryBuilder) /** * Build the sub query for a comparing operator like =,<,>. * - * @param array $operation The operation to apply. - * @param QueryBuilder $queryBuilder The query builder. + * @param TSQLFilterCMP $operation The operation to apply. + * @param QueryBuilder $queryBuilder The query builder. * * @return string - * - * @internal param array $params The parameters of the entire query. */ - private static function filterComparing($operation, QueryBuilder $queryBuilder) + private static function filterComparing(array $operation, QueryBuilder $queryBuilder): string { return $queryBuilder ->expr() @@ -344,16 +348,14 @@ private static function filterComparing($operation, QueryBuilder $queryBuilder) /** * Return the filter query for a "foo IN ('a', 'b')" filter. * - * @param array $operation The operation to apply. + * @param TSQLFilterIN $operation The operation to apply. * @param QueryBuilder $queryBuilder The query builder. * * @return string - * - * @internal param array $params The parameters of the entire query. */ - private static function filterInOrNotInList($operation, QueryBuilder $queryBuilder) + private static function filterInOrNotInList(array $operation, QueryBuilder $queryBuilder): string { - $expressionMethod = lcfirst(preg_replace('/\s+/', '', ucwords(strtolower($operation['operation'])))); + $expressionMethod = \lcfirst(\preg_replace('/\s+/', '', \ucwords(\strtolower($operation['operation'])))); $values = []; foreach ($operation['values'] as $index => $value) { @@ -372,16 +374,14 @@ private static function filterInOrNotInList($operation, QueryBuilder $queryBuild * * The searched value may contain the wildcards '*' and '?' which will get converted to proper SQL. * - * @param array $operation The operation to apply. - * @param QueryBuilder $queryBuilder The query builder. + * @param TSQLFilterLIKE $operation The operation to apply. + * @param QueryBuilder $queryBuilder The query builder. * * @return string - * - * @internal param array $params The parameters of the entire query. */ - private static function filterLikeOrNotLike($operation, QueryBuilder $queryBuilder) + private static function filterLikeOrNotLike(array $operation, QueryBuilder $queryBuilder): string { - $wildcards = str_replace(array('*', '?'), array('%', '_'), $operation['value']); + $wildcards = \str_replace(['*', '?'], ['%', '_'], $operation['value']); return $queryBuilder ->expr() @@ -389,20 +389,18 @@ private static function filterLikeOrNotLike($operation, QueryBuilder $queryBuild } /** - * Return the filter query for a "foo LIKE '%ba_r%'" filter. + * Return the filter query for a "foo IS NULL" filter. * * The searched value may contain the wildcards '*' and '?' which will get converted to proper SQL. * - * @param array $operation The operation to apply. - * @param QueryBuilder $queryBuilder The query builder. + * @param TSQLFilterNULL $operation The operation to apply. + * @param QueryBuilder $queryBuilder The query builder. * * @return string - * - * @internal param array $params The parameters of the entire query. */ - private static function filterIsNullOrIsNotNull($operation, QueryBuilder $queryBuilder) + private static function filterIsNullOrIsNotNull(array $operation, QueryBuilder $queryBuilder): string { - $expressionMethod = lcfirst(preg_replace('/\s+/', '', ucwords(strtolower($operation['operation'])))); + $expressionMethod = \lcfirst(\preg_replace('/\s+/', '', \ucwords(\strtolower($operation['operation'])))); return $queryBuilder ->expr() diff --git a/src/Data/DefaultDataProviderSqlUtils.php b/src/Data/DefaultDataProviderSqlUtils.php index 2faadf72..e3c0b928 100644 --- a/src/Data/DefaultDataProviderSqlUtils.php +++ b/src/Data/DefaultDataProviderSqlUtils.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,8 @@ * @package contao-community-alliance/dc-general * @author Christian Schiffler * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -35,7 +36,6 @@ class DefaultDataProviderSqlUtils * Returns all values from $objConfig->getFields() as comma separated list. * * @param ConfigInterface $config The configuration to use. - * * @param string $idProperty The name of the id property. * * @return string @@ -47,8 +47,9 @@ public static function buildFieldQuery($config, $idProperty) if ($config->getIdOnly()) { return $idProperty; } + if (null !== $config->getFields()) { - $fields = \implode(', ', $config->getFields()); + $fields = \implode(', ', (array) $config->getFields()); if (false !== \stripos($fields, 'DISTINCT')) { return $fields; } @@ -62,7 +63,6 @@ public static function buildFieldQuery($config, $idProperty) * Build the WHERE clause for a configuration. * * @param ConfigInterface $config The configuration to use. - * * @param array $parameters The query parameters will get stored into this array. * * @return string The combined WHERE clause (including the word "WHERE"). @@ -94,7 +94,7 @@ public static function buildSortingQuery($config) $result = ''; $fields = []; - if (empty($sorting) || !\is_array($sorting)) { + if (empty($sorting)) { return ''; } foreach ($sorting as $field => $direction) { @@ -169,12 +169,8 @@ private static function buildFilterQuery($config, array &$parameters) * * @throws DcGeneralRuntimeException If an invalid filter entry is encountered. */ - private static function calculateSubfilter($filter, array &$parameters) + private static function calculateSubfilter(array $filter, array &$parameters): string { - if (!\is_array($filter)) { - throw new DcGeneralRuntimeException('Error Processing sub filter: ' . \var_export($filter, true), 1); - } - switch ($filter['operation']) { case 'AND': case 'OR': @@ -264,9 +260,11 @@ private static function filterInList($operation, &$params) */ private static function filterLike($operation, &$params) { - $wildcards = \str_replace(['*', '?'], ['%', '_'], $operation['value']); + $value = $operation['value']; + assert(\is_string($value)); + $wildcards = \str_replace(['*', '?'], ['%', '_'], $value); $params[] = $wildcards; - return \sprintf('(%s LIKE ?)', $operation['property'], $wildcards); + return \sprintf('(%s LIKE %s)', $operation['property'], $wildcards); } } diff --git a/src/Data/DefaultEditInformation.php b/src/Data/DefaultEditInformation.php index 6655e2fd..e0677b5c 100644 --- a/src/Data/DefaultEditInformation.php +++ b/src/Data/DefaultEditInformation.php @@ -37,7 +37,7 @@ class DefaultEditInformation implements EditInformationInterface /** * The model errors. * - * @var array + * @var array>> */ protected $modelErrors = []; @@ -71,11 +71,7 @@ public function getModelError(ModelInterface $model) { $modelId = ModelId::fromModel($model); - if (!isset($this->modelErrors[$modelId->getSerialized()])) { - return null; - } - - return $this->modelErrors[$modelId->getSerialized()]; + return $this->modelErrors[$modelId->getSerialized()] ?? []; } /** @@ -83,23 +79,24 @@ public function getModelError(ModelInterface $model) */ public function setModelError(ModelInterface $model, array $error, PropertyInterface $property) { - $modelId = ModelId::fromModel($model); + $modelId = ModelId::fromModel($model)->getSerialized(); + $propName = $property->getName(); - if (!isset($this->models[$modelId->getSerialized()])) { - $this->models[$modelId->getSerialized()] = $model; + if (!isset($this->models[$modelId])) { + $this->models[$modelId] = $model; } - if (!isset($this->modelErrors[$modelId->getSerialized()])) { - $this->modelErrors[$modelId->getSerialized()] = []; + if (!isset($this->modelErrors[$modelId])) { + $this->modelErrors[$modelId] = []; } - if (isset($this->modelErrors[$modelId->getSerialized()][$property->getName()])) { - $this->modelErrors[$modelId->getSerialized()][$property->getName()] = \array_merge( - (array) $this->modelErrors[$modelId->getSerialized()][$property->getName()], + if (isset($this->modelErrors[$modelId][$propName])) { + $this->modelErrors[$modelId][$propName] = \array_merge( + $this->modelErrors[$modelId][$propName], $error ); } else { - $this->modelErrors[$modelId->getSerialized()][$property->getName()] = $error; + $this->modelErrors[$modelId][$propName] = $error; } } diff --git a/src/Data/DefaultFilterOptionCollection.php b/src/Data/DefaultFilterOptionCollection.php index 394fd3b7..1941aac3 100644 --- a/src/Data/DefaultFilterOptionCollection.php +++ b/src/Data/DefaultFilterOptionCollection.php @@ -30,7 +30,7 @@ class DefaultFilterOptionCollection implements FilterOptionCollectionInterface /** * The language information stored in this collection. * - * @var array + * @var array */ protected $filterValues = []; @@ -47,7 +47,7 @@ public function add($filterKey, $filterValue) /** * Get a iterator for this collection. * - * @return \Traversable + * @return \Traversable */ public function getIterator(): \Traversable { diff --git a/src/Data/DefaultLanguageInformation.php b/src/Data/DefaultLanguageInformation.php index 4ca04e5e..b0c55b8b 100644 --- a/src/Data/DefaultLanguageInformation.php +++ b/src/Data/DefaultLanguageInformation.php @@ -35,7 +35,7 @@ class DefaultLanguageInformation implements LanguageInformationInterface /** * The ISO 3166 country code. * - * @var string + * @var null|string */ protected $country; @@ -72,8 +72,8 @@ public function getCountryCode() */ public function getLocale() { - if ($this->getCountryCode()) { - return $this->getLanguageCode() . '_' . $this->getCountryCode(); + if (null !== $country = $this->getCountryCode()) { + return $this->getLanguageCode() . '_' . $country; } return $this->getLanguageCode(); diff --git a/src/Data/DefaultLanguageInformationCollection.php b/src/Data/DefaultLanguageInformationCollection.php index ab9ad230..e03a17c8 100644 --- a/src/Data/DefaultLanguageInformationCollection.php +++ b/src/Data/DefaultLanguageInformationCollection.php @@ -29,7 +29,7 @@ class DefaultLanguageInformationCollection implements LanguageInformationCollect /** * The language information stored in this collection. * - * @var LanguageInformationInterface[] + * @var list */ protected $languages = []; diff --git a/src/Data/DefaultModel.php b/src/Data/DefaultModel.php index 85245918..887de68b 100644 --- a/src/Data/DefaultModel.php +++ b/src/Data/DefaultModel.php @@ -17,6 +17,7 @@ * @author Andreas Isaak * @author Patrick Kahl * @author Sven Baumann + * @author Ingolf Steinhardt * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource @@ -36,12 +37,12 @@ class DefaultModel extends AbstractModel /** * A list with all properties. * - * @var array + * @var array */ protected $arrProperties = []; /** - * The Id of this model. + * The ID of this model. * * @var mixed */ @@ -52,7 +53,7 @@ class DefaultModel extends AbstractModel * * @var string */ - protected $strProviderName; + protected $strProviderName = ''; /** * Copy this model, without the id. @@ -109,18 +110,18 @@ public function getPropertiesAsArray() /** * Set the id for this object. * - * NOTE: when the Id has been set once to a non null value, it can NOT be changed anymore. + * NOTE: when the ID has been set once to a non-null value, it can NOT be changed anymore. * - * Normally this should only be called from inside of the implementing provider. + * Normally this should only be called from inside the implementing provider. * - * @param mixed $mixID Could be a integer, string or anything else - depends on the provider implementation. + * @param mixed $mixId Could be integer, string or anything else - depends on the provider implementation. * * @return void */ - public function setID($mixID) + public function setId($mixId) { if (null === $this->mixID) { - $this->setIdRaw($mixID); + $this->setIdRaw($mixId); $this->setMeta(static::IS_CHANGED, true); } } @@ -130,13 +131,13 @@ public function setID($mixID) * * This method is not interfaced and MUST only be used for initial values from the data provider. * - * @param mixed $mixID Could be a integer, string or anything else - depends on the provider implementation. + * @param mixed $mixId Could be a integer, string or anything else - depends on the provider implementation. * * @return void */ - public function setIdRaw($mixID) + public function setIdRaw($mixId) { - $this->mixID = $mixID; + $this->mixID = $mixId; } /** @@ -157,16 +158,16 @@ public function setPropertyRaw($propertyName, $value) /** * Update the property value in the model. * - * @param string $propertyName The property name to be set. - * @param mixed $value The value to be set. + * @param string $strPropertyName The property name to be set. + * @param mixed $varValue The value to be set. * * @return void */ - public function setProperty($propertyName, $value) + public function setProperty($strPropertyName, $varValue) { - if ($value !== $this->getProperty($propertyName)) { + if ($varValue !== $this->getProperty($strPropertyName)) { $this->setMeta(static::IS_CHANGED, true); - $this->setPropertyRaw($propertyName, $value); + $this->setPropertyRaw($strPropertyName, $varValue); } } @@ -179,14 +180,12 @@ public function setProperty($propertyName, $value) */ public function setPropertiesAsArray($properties) { - if (\is_array($properties)) { - if (\array_key_exists('id', $properties)) { - unset($properties['id']); - } + if (\array_key_exists('id', $properties)) { + unset($properties['id']); + } - foreach ($properties as $propertyName => $value) { - $this->setProperty($propertyName, $value); - } + foreach ($properties as $propertyName => $value) { + $this->setProperty($propertyName, $value); } } diff --git a/src/Data/EditInformationInterface.php b/src/Data/EditInformationInterface.php index 8093912b..4d8a2ddb 100644 --- a/src/Data/EditInformationInterface.php +++ b/src/Data/EditInformationInterface.php @@ -39,7 +39,7 @@ public function hasAnyModelError(); * * @param ModelInterface $model The model. * - * @return array + * @return array> */ public function getModelError(ModelInterface $model); @@ -47,7 +47,7 @@ public function getModelError(ModelInterface $model); * Set the error information for the model. * * @param ModelInterface $model The model. - * @param array $error The error information. + * @param list $error The error information. * @param PropertyInterface $property The property. * * @return void diff --git a/src/Data/LanguageInformationCollectionInterface.php b/src/Data/LanguageInformationCollectionInterface.php index 3b9d34d4..04e8600b 100644 --- a/src/Data/LanguageInformationCollectionInterface.php +++ b/src/Data/LanguageInformationCollectionInterface.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,8 @@ * @package contao-community-alliance/dc-general * @author Christian Schiffler * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -22,6 +23,8 @@ /** * This represents an iterable collection of Model elements. + * + * @extends \IteratorAggregate */ interface LanguageInformationCollectionInterface extends \IteratorAggregate, \Countable { diff --git a/src/Data/LanguageInformationInterface.php b/src/Data/LanguageInformationInterface.php index 3575469d..6d2e85dc 100644 --- a/src/Data/LanguageInformationInterface.php +++ b/src/Data/LanguageInformationInterface.php @@ -35,7 +35,7 @@ public function getLanguageCode(); /** * Retrieve the ISO 3166 country code. * - * @return string + * @return string|null */ public function getCountryCode(); diff --git a/src/Data/ModelId.php b/src/Data/ModelId.php index bffd4428..1fb554e9 100644 --- a/src/Data/ModelId.php +++ b/src/Data/ModelId.php @@ -90,6 +90,7 @@ public function getId() */ public static function fromValues($dataProviderName, $modelId) { + /** @psalm-suppress UnsafeInstantiation */ return new static($dataProviderName, $modelId); } diff --git a/src/Data/ModelInterface.php b/src/Data/ModelInterface.php index 6bfe1251..37e2a979 100644 --- a/src/Data/ModelInterface.php +++ b/src/Data/ModelInterface.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -15,7 +15,8 @@ * @author Stefan Heimes * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -28,6 +29,8 @@ * Interface ModelInterface. * * This interface describes a model used in data providers. + * + * @extends \IteratorAggregate */ interface ModelInterface extends \IteratorAggregate { @@ -115,14 +118,14 @@ public function getId(); * * This method returns null if an unknown property is retrieved. * - * @param string $strPropertyName The property name to be retrieved. + * @param string $propertyName The property name to be retrieved. * * @return mixed The value of the given property. */ - public function getProperty($strPropertyName); + public function getProperty($propertyName); /** - * Fetch all properties from the model as an name => value array. + * Fetch all properties from the model as a name => value array. * * @return array */ @@ -140,11 +143,11 @@ public function getMeta($strMetaName); /** * Set the id for this object. * - * NOTE: when the Id has been set once to a non null value, it can NOT be changed anymore. + * NOTE: when the ID has been set once to a non-null value, it can NOT be changed anymore. * - * Normally this should only be called from inside of the implementing provider. + * Normally this should only be called from inside the implementing provider. * - * @param mixed $mixId Could be a integer, string or anything else - depends on the provider implementation. + * @param mixed $mixId Could be integer, string or anything else - depends on the provider implementation. * * @return void */ @@ -163,11 +166,11 @@ public function setProperty($strPropertyName, $varValue); /** * Update all properties in the model. * - * @param array $arrProperties The property values as name => value pairs. + * @param array $properties The property values as name => value pairs. * * @return void */ - public function setPropertiesAsArray($arrProperties); + public function setPropertiesAsArray($properties); /** * Update meta information in the model. diff --git a/src/Data/MultiLanguageDataProvider.php b/src/Data/MultiLanguageDataProvider.php index c90e8c3b..a0f93f48 100644 --- a/src/Data/MultiLanguageDataProvider.php +++ b/src/Data/MultiLanguageDataProvider.php @@ -28,6 +28,8 @@ * Implementation of a multi language Contao database data provider. * * The default language will be initialized to "en". + * + * @psalm-suppress PropertyNotSetInConstructor - properties will get set in setBaseConfig(). */ class MultiLanguageDataProvider extends DefaultDataProvider implements MultiLanguageDataProviderInterface { diff --git a/src/Data/MultiLanguageDataProviderInterface.php b/src/Data/MultiLanguageDataProviderInterface.php index bbdd5e69..4759735c 100644 --- a/src/Data/MultiLanguageDataProviderInterface.php +++ b/src/Data/MultiLanguageDataProviderInterface.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -49,11 +50,11 @@ public function getFallbackLanguage($mixID); /** * Set the current working language for the whole data provider. * - * @param string $strLanguage The new language, use short tag "2 chars like de, fr etc.". + * @param string $language The new language, use short tag "2 chars like de, fr etc.". * * @return DataProviderInterface */ - public function setCurrentLanguage($strLanguage); + public function setCurrentLanguage($language); /** * Get the current working language. diff --git a/src/Data/NoOpDataProvider.php b/src/Data/NoOpDataProvider.php index 6beb8f0a..02bc7782 100644 --- a/src/Data/NoOpDataProvider.php +++ b/src/Data/NoOpDataProvider.php @@ -27,6 +27,8 @@ * * Base implementation of an no operational data provider. This data provider is simply a stub endpoint without any * logic at all. It is useful as parent class for drivers that only implement a fraction of all DcGeneral features. + * + * @psalm-suppress MissingConstructor - properties will get set in setBaseConfig(). */ class NoOpDataProvider implements DataProviderInterface { @@ -72,7 +74,7 @@ public function getEmptyCollection() } /** - * {@inheritdoc} + * @return DefaultFilterOptionCollection */ public function getEmptyFilterOptionCollection() { @@ -122,6 +124,7 @@ public function getCount(ConfigInterface $config) */ public function save(ModelInterface $item, $timestamp = 0) { + return $item; } /** @@ -161,7 +164,7 @@ public function getVersion($mixID, $mixVersion) */ public function getVersions($mixID, $onlyActive = false) { - return null; + return new DefaultCollection(); } /** diff --git a/src/Data/PropertyValueBag.php b/src/Data/PropertyValueBag.php index 166592c9..a9f9b043 100644 --- a/src/Data/PropertyValueBag.php +++ b/src/Data/PropertyValueBag.php @@ -34,27 +34,27 @@ class PropertyValueBag implements PropertyValueBagInterface /** * All properties and its values in this bag. * - * @var array + * @var array */ protected $properties = []; /** * All properties that are marked as invalid and their error messages. * - * @var array + * @var array> */ protected $errors = []; /** * Create a new instance of a property bag. * - * @param array|null $properties The initial property values to use. + * @param iterable|null|mixed $properties The initial property values to use. * * @throws DcGeneralInvalidArgumentException If the passed properties aren't null or an array. */ public function __construct($properties = null) { - if (\is_array($properties) || $properties instanceof \Traversable) { + if (\is_iterable($properties)) { foreach ($properties as $property => $value) { $this->setPropertyValue($property, $value); } @@ -200,7 +200,7 @@ public function getInvalidPropertyNames() public function getPropertyValueErrors($property) { $this->requirePropertyValue($property); - return (array) ($this->errors[$property] ?? []); + return $this->errors[$property] ?? []; } /** @@ -248,6 +248,7 @@ public function offsetGet($offset): mixed */ public function offsetSet($offset, $value): void { + assert(\is_string($offset)); $this->setPropertyValue($offset, $value); } @@ -260,7 +261,7 @@ public function offsetUnset($offset): void } /** - * {@inheritdoc} + * @param string $name */ public function __isset($name) { @@ -268,7 +269,7 @@ public function __isset($name) } /** - * {@inheritdoc} + * @param string $name */ public function __get($name) { @@ -276,7 +277,8 @@ public function __get($name) } /** - * {@inheritdoc} + * @param string $name + * @param mixed $value */ public function __set($name, $value) { @@ -284,7 +286,7 @@ public function __set($name, $value) } /** - * {@inheritdoc} + * @param string $name */ public function __unset($name) { @@ -294,7 +296,7 @@ public function __unset($name) /** * Exports the {@link PropertyValueBag} to an array. * - * @return array + * @return array */ public function getArrayCopy() { diff --git a/src/Data/PropertyValueBagInterface.php b/src/Data/PropertyValueBagInterface.php index 100b5f30..41a7de09 100644 --- a/src/Data/PropertyValueBagInterface.php +++ b/src/Data/PropertyValueBagInterface.php @@ -24,6 +24,9 @@ /** * A generic bag containing properties and their values. + * + * @extends \IteratorAggregate + * @extends \ArrayAccess */ interface PropertyValueBagInterface extends \IteratorAggregate, \Countable, \ArrayAccess { @@ -103,9 +106,9 @@ public function isPropertyValueValid($property); /** * Mark a property as invalid and add an error message to the property. * - * @param string $property The name of the property to mark. - * @param string|array|mixed $error The error message to attach for this property. - * @param bool $append Append this error and keep previous errors (optional). + * @param string $property The name of the property to mark. + * @param string|list $error The error message to attach for this property. + * @param bool $append Append this error and keep previous errors (optional). * * @return PropertyValueBag */ @@ -132,7 +135,7 @@ public function getInvalidPropertyNames(); * * @param string $property The name of the property to retrieve the errors for. * - * @return array + * @return list */ public function getPropertyValueErrors($property); @@ -146,7 +149,7 @@ public function getInvalidPropertyErrors(); /** * Exports the {@link PropertyValueBag} to an array. * - * @return array + * @return array */ public function getArrayCopy(); } diff --git a/src/Data/TableRowsAsRecordsDataProvider.php b/src/Data/TableRowsAsRecordsDataProvider.php index 86fe9a2a..2a789e93 100644 --- a/src/Data/TableRowsAsRecordsDataProvider.php +++ b/src/Data/TableRowsAsRecordsDataProvider.php @@ -31,6 +31,8 @@ * Class TableRowsAsRecordsDataProvider. * * This data provider allows to map multiple rows of a SQL table into a single model for usage in a MultiColumnWizard. + * + * @psalm-suppress MissingConstructor - properties will get set in setBaseConfig(). */ class TableRowsAsRecordsDataProvider extends DefaultDataProvider { @@ -102,7 +104,7 @@ public function getSortingColumnProperty() * @throws DcGeneralException Throws always an exception telling that the method (see param $strMethod) must not be * called. * - * @return void + * @return never */ protected function youShouldNotCallMe($strMethod) { @@ -120,7 +122,7 @@ protected function youShouldNotCallMe($strMethod) * * @param mixed $item Unused. * - * @return void + * @return never * * @throws DcGeneralException Always throws exception. * @@ -164,6 +166,7 @@ public function fetch(ConfigInterface $config) $statement = $queryBuilder->executeQuery(); $model = $this->getEmptyModel(); + assert($model instanceof DefaultModel); $model->setID($config->getId()); if (0 < $statement->rowCount()) { $model->setPropertyRaw('rows', $statement->fetchAllAssociative()); @@ -177,7 +180,7 @@ public function fetch(ConfigInterface $config) * * @param ConfigInterface $config Unused. * - * @return void + * @return never * * @throws DcGeneralException Always throws exception. * @@ -193,7 +196,7 @@ public function fetchAll(ConfigInterface $config) * * @param ConfigInterface $config Unused. * - * @return void + * @return never * * @throws DcGeneralException Always throws exception. * @@ -211,7 +214,7 @@ public function getCount(ConfigInterface $config) * @param mixed $new Unused. * @param int $primaryId Unused. * - * @return void + * @return never * * @throws DcGeneralException Always throws exception. * @@ -227,7 +230,7 @@ public function isUniqueValue($field, $new, $primaryId = null) * * @param string $field Unused. * - * @return void + * @return never * * @throws DcGeneralException Always throws exception. * @@ -262,9 +265,6 @@ public function resetFallback($field) */ public function save(ModelInterface $item, $timestamp = 0, $recursive = false) { - if (!\is_int($timestamp)) { - throw new DcGeneralException('The parameter for this method has been change!'); - } $data = $item->getProperty('rows'); if (!($data && $item->getId())) { throw new DcGeneralException('invalid input data in model.', 1); @@ -292,7 +292,7 @@ public function save(ModelInterface $item, $timestamp = 0, $recursive = false) $sqlData, ['id' => $intId, $this->strGroupCol => $item->getId()] ); - $keep[] = $intId; + $keep[] = (string) $intId; continue; } @@ -301,8 +301,10 @@ public function save(ModelInterface $item, $timestamp = 0, $recursive = false) $sqlData[$this->strGroupCol] = $item->getId(); $this->connection->insert($this->source, $sqlData); - - $keep[] = $this->connection->lastInsertId($this->source); + if (false === $lastInsertId = $this->connection->lastInsertId($this->source)) { + throw new \RuntimeException('Failed to insert'); + } + $keep[] = (string) $lastInsertId; } // House keeping, kill the rest. @@ -323,7 +325,7 @@ public function save(ModelInterface $item, $timestamp = 0, $recursive = false) * @param CollectionInterface $items Unused. * @param int $timestamp Optional the timestamp. * - * @return void + * @return never * * @throws DcGeneralException Always throws exception. * @@ -354,7 +356,7 @@ public function fieldExists($columnName) * @param mixed $mixID Unused. * @param mixed $mixVersion Unused. * - * @return void + * @return never * * @throws DcGeneralException Always throws exception. * @@ -371,14 +373,14 @@ public function getVersion($mixID, $mixVersion) * @param mixed $mixID Unused. * @param boolean $onlyActive Unused. * - * @return null + * @return CollectionInterface * * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function getVersions($mixID, $onlyActive = false) { // Sorry, versioning not supported. - return null; + return new DefaultCollection(); } /** @@ -387,7 +389,7 @@ public function getVersions($mixID, $onlyActive = false) * @param ModelInterface $model Unused. * @param string $username Unused. * - * @return void + * @return never * * @throws DcGeneralException Always throws exception. * @@ -404,7 +406,7 @@ public function saveVersion(ModelInterface $model, $username) * @param mixed $mixID Unused. * @param mixed $mixVersion Unused. * - * @return void + * @return never * * @throws DcGeneralException Always throws exception. * @@ -420,7 +422,7 @@ public function setVersionActive($mixID, $mixVersion) * * @param mixed $mixID Unused. * - * @return void + * @return never * * @throws DcGeneralException Always throws exception. * @@ -437,7 +439,7 @@ public function getActiveVersion($mixID) * @param ModelInterface $firstModel Unused. * @param ModelInterface $secondModel Unused. * - * @return void + * @return never * * @throws DcGeneralException Always throws exception. * @@ -455,7 +457,7 @@ public function sameModels($firstModel, $secondModel) * @param string $saveSQL Unused. * @param string $table Unused. * - * @return void + * @return never * * @throws DcGeneralException Always throws exception. * diff --git a/src/DataDefinition/AbstractConditionChain.php b/src/DataDefinition/AbstractConditionChain.php index 7facf388..1deda72f 100644 --- a/src/DataDefinition/AbstractConditionChain.php +++ b/src/DataDefinition/AbstractConditionChain.php @@ -122,7 +122,7 @@ public function setConjunction($conjunction) ); } - $this->conjunction = (string) $conjunction; + $this->conjunction = $conjunction; return $this; } diff --git a/src/DataDefinition/Builder/AbstractEventDrivenDataDefinitionBuilder.php b/src/DataDefinition/Builder/AbstractEventDrivenDataDefinitionBuilder.php index 2fc3bb39..78e6fee7 100644 --- a/src/DataDefinition/Builder/AbstractEventDrivenDataDefinitionBuilder.php +++ b/src/DataDefinition/Builder/AbstractEventDrivenDataDefinitionBuilder.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -22,6 +23,7 @@ namespace ContaoCommunityAlliance\DcGeneral\DataDefinition\Builder; use ContaoCommunityAlliance\DcGeneral\Factory\Event\BuildDataDefinitionEvent; +use LogicException; use Symfony\Component\EventDispatcher\EventDispatcherInterface; /** @@ -30,6 +32,8 @@ * Abstract base class for an data definition builder. * * To use it, implement the method build() and register the class to the event dispatcher. + * + * @psalm-suppress MissingConstructor - properties will get set in process(). */ abstract class AbstractEventDrivenDataDefinitionBuilder implements DataDefinitionBuilderInterface { @@ -44,16 +48,16 @@ abstract class AbstractEventDrivenDataDefinitionBuilder implements DataDefinitio /** * The event dispatcher currently calling. * - * @var EventDispatcherInterface + * @var EventDispatcherInterface|null */ - protected $dispatcher; + protected $dispatcher = null; /** * The name of the called event. * * @var string */ - protected $eventName; + protected $eventName = ''; /** * Retrieve the dispatcher. @@ -62,6 +66,10 @@ abstract class AbstractEventDrivenDataDefinitionBuilder implements DataDefinitio */ protected function getDispatcher() { + if (null === $this->dispatcher) { + throw new LogicException('Dispatcher not set.'); + } + return $this->dispatcher; } @@ -89,6 +97,7 @@ protected function getDispatchedEventName() */ public static function process(BuildDataDefinitionEvent $event, $eventName, $dispatcher) { + /** @psalm-suppress UnsafeInstantiation */ $builder = new static(); $builder->eventName = $eventName; $builder->dispatcher = $dispatcher; diff --git a/src/DataDefinition/DataProviderInformation.php b/src/DataDefinition/DataProviderInformation.php index adb9f287..457788f8 100644 --- a/src/DataDefinition/DataProviderInformation.php +++ b/src/DataDefinition/DataProviderInformation.php @@ -30,14 +30,14 @@ class DataProviderInformation implements DataProviderInformationInterface * * @var string */ - protected $name; + protected $name = ''; /** * Flag determining if versioning is enabled for this provider or not. * * @var bool */ - protected $versioningEnabled; + protected $versioningEnabled = false; /** * {@inheritdoc} diff --git a/src/DataDefinition/DefaultContainer.php b/src/DataDefinition/DefaultContainer.php index fd77d85a..5cf9b821 100644 --- a/src/DataDefinition/DefaultContainer.php +++ b/src/DataDefinition/DefaultContainer.php @@ -57,7 +57,7 @@ class DefaultContainer implements ContainerInterface */ public function __construct($name) { - $this->name = (string) $name; + $this->name = $name; } /** @@ -171,9 +171,9 @@ public function hasBasicDefinition() /** * {@inheritdoc} */ - public function setBasicDefinition(BasicDefinitionInterface $definition) + public function setBasicDefinition(BasicDefinitionInterface $basicDefinition) { - return $this->setDefinition(BasicDefinitionInterface::NAME, $definition); + return $this->setDefinition(BasicDefinitionInterface::NAME, $basicDefinition); } /** @@ -181,7 +181,10 @@ public function setBasicDefinition(BasicDefinitionInterface $definition) */ public function getBasicDefinition() { - return $this->getDefinition(BasicDefinitionInterface::NAME); + $definition = $this->getDefinition(BasicDefinitionInterface::NAME); + assert($definition instanceof BasicDefinitionInterface); + + return $definition; } /** @@ -195,9 +198,9 @@ public function hasPropertiesDefinition() /** * {@inheritdoc} */ - public function setPropertiesDefinition(PropertiesDefinitionInterface $definition) + public function setPropertiesDefinition(PropertiesDefinitionInterface $propertiesDefinition) { - return $this->setDefinition(PropertiesDefinitionInterface::NAME, $definition); + return $this->setDefinition(PropertiesDefinitionInterface::NAME, $propertiesDefinition); } /** @@ -205,7 +208,10 @@ public function setPropertiesDefinition(PropertiesDefinitionInterface $definitio */ public function getPropertiesDefinition() { - return $this->getDefinition(PropertiesDefinitionInterface::NAME); + $definition = $this->getDefinition(PropertiesDefinitionInterface::NAME); + assert($definition instanceof PropertiesDefinitionInterface); + + return $definition; } /** @@ -219,9 +225,9 @@ public function hasPalettesDefinition() /** * {@inheritdoc} */ - public function setPalettesDefinition(PalettesDefinitionInterface $definition) + public function setPalettesDefinition(PalettesDefinitionInterface $palettesDefinition) { - return $this->setDefinition(PalettesDefinitionInterface::NAME, $definition); + return $this->setDefinition(PalettesDefinitionInterface::NAME, $palettesDefinition); } /** @@ -229,7 +235,10 @@ public function setPalettesDefinition(PalettesDefinitionInterface $definition) */ public function getPalettesDefinition() { - return $this->getDefinition(PalettesDefinitionInterface::NAME); + $definition = $this->getDefinition(PalettesDefinitionInterface::NAME); + assert($definition instanceof PalettesDefinitionInterface); + + return $definition; } /** @@ -253,7 +262,10 @@ public function setDataProviderDefinition(DataProviderDefinitionInterface $defin */ public function getDataProviderDefinition() { - return $this->getDefinition(DataProviderDefinitionInterface::NAME); + $definition = $this->getDefinition(DataProviderDefinitionInterface::NAME); + assert($definition instanceof DataProviderDefinitionInterface); + + return $definition; } /** @@ -277,7 +289,10 @@ public function setModelRelationshipDefinition(ModelRelationshipDefinitionInterf */ public function getModelRelationshipDefinition() { - return $this->getDefinition(ModelRelationshipDefinitionInterface::NAME); + $definition = $this->getDefinition(ModelRelationshipDefinitionInterface::NAME); + assert($definition instanceof ModelRelationshipDefinitionInterface); + + return $definition; } /** diff --git a/src/DataDefinition/Definition/BasicDefinitionInterface.php b/src/DataDefinition/Definition/BasicDefinitionInterface.php index 96db9770..85429a36 100644 --- a/src/DataDefinition/Definition/BasicDefinitionInterface.php +++ b/src/DataDefinition/Definition/BasicDefinitionInterface.php @@ -276,7 +276,7 @@ public function getRootEntries(); * * @param bool $dynamicParentTable The flag - if the data container is an dynamic. * - * @return bool + * @return BasicDefinitionInterface */ public function setDynamicParentTable($dynamicParentTable); diff --git a/src/DataDefinition/Definition/DataProviderDefinitionInterface.php b/src/DataDefinition/Definition/DataProviderDefinitionInterface.php index 61b93c42..aefedf94 100644 --- a/src/DataDefinition/Definition/DataProviderDefinitionInterface.php +++ b/src/DataDefinition/Definition/DataProviderDefinitionInterface.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -25,6 +26,9 @@ /** * This interface describes a collection of data provider information. + * + * @extends \IteratorAggregate + * @extends \ArrayAccess */ interface DataProviderDefinitionInterface extends DefinitionInterface, \IteratorAggregate, \Countable, \ArrayAccess { diff --git a/src/DataDefinition/Definition/DefaultBasicDefinition.php b/src/DataDefinition/Definition/DefaultBasicDefinition.php index 66e84858..820a1ba3 100644 --- a/src/DataDefinition/Definition/DefaultBasicDefinition.php +++ b/src/DataDefinition/Definition/DefaultBasicDefinition.php @@ -94,9 +94,9 @@ class DefaultBasicDefinition implements BasicDefinitionInterface /** * Determines if the view shall switch automatically into edit mode. * - * @var bool|null + * @var bool */ - protected $switchToEditEnabled; + protected $switchToEditEnabled = false; /** * The ids of the root entries. @@ -224,6 +224,8 @@ public function getAdditionalFilter($dataProvider = null) public function setEditOnlyMode($value) { $this->isEditOnlyMode = $value; + + return $this; } /** diff --git a/src/DataDefinition/Definition/DefaultDataProviderDefinition.php b/src/DataDefinition/Definition/DefaultDataProviderDefinition.php index 18bc04a6..e7d72205 100644 --- a/src/DataDefinition/Definition/DefaultDataProviderDefinition.php +++ b/src/DataDefinition/Definition/DefaultDataProviderDefinition.php @@ -35,7 +35,7 @@ class DefaultDataProviderDefinition implements DataProviderDefinitionInterface /** * The data provider information stored in the definition. * - * @var DataProviderInformationInterface[] + * @var array */ protected $information = []; @@ -58,6 +58,8 @@ public function addInformation($information) } $this->information[$name] = $information; + + return $this; } /** @@ -80,10 +82,6 @@ protected function makeName($information) $information = $information->getName(); } - if (!\is_string($information)) { - throw new DcGeneralInvalidArgumentException('Invalid value passed.'); - } - return $information; } @@ -93,6 +91,8 @@ protected function makeName($information) public function removeInformation($information) { unset($this->information[$this->makeName($information)]); + + return $this; } /** @@ -101,6 +101,8 @@ public function removeInformation($information) public function setInformation($name, $information) { $this->information[$name] = $information; + + return $this; } /** @@ -164,6 +166,7 @@ public function offsetGet($offset): mixed */ public function offsetSet($offset, $value): void { + assert(\is_string($offset)); $this->setInformation($offset, $value); } @@ -176,7 +179,7 @@ public function offsetUnset($offset): void } /** - * {@inheritdoc} + * @param string $name */ public function __isset($name) { @@ -184,7 +187,7 @@ public function __isset($name) } /** - * {@inheritdoc} + * @param string $name */ public function __get($name) { @@ -192,7 +195,8 @@ public function __get($name) } /** - * {@inheritdoc} + * @param string $name + * @param mixed $value */ public function __set($name, $value) { @@ -200,7 +204,7 @@ public function __set($name, $value) } /** - * {@inheritdoc} + * @param string $name */ public function __unset($name) { diff --git a/src/DataDefinition/Definition/DefaultModelRelationshipDefinition.php b/src/DataDefinition/Definition/DefaultModelRelationshipDefinition.php index 151ae9cf..54db3a07 100644 --- a/src/DataDefinition/Definition/DefaultModelRelationshipDefinition.php +++ b/src/DataDefinition/Definition/DefaultModelRelationshipDefinition.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,8 @@ * @package contao-community-alliance/dc-general * @author Christian Schiffler * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -31,9 +32,9 @@ class DefaultModelRelationshipDefinition implements ModelRelationshipDefinitionI /** * The root condition relationship. * - * @var RootConditionInterface + * @var RootConditionInterface|null */ - protected $rootCondition; + protected $rootCondition = null; /** * A collection of parent child conditions. diff --git a/src/DataDefinition/Definition/DefaultPropertiesDefinition.php b/src/DataDefinition/Definition/DefaultPropertiesDefinition.php index 0f655987..770913ea 100644 --- a/src/DataDefinition/Definition/DefaultPropertiesDefinition.php +++ b/src/DataDefinition/Definition/DefaultPropertiesDefinition.php @@ -106,7 +106,7 @@ public function hasProperty($name) /** * {@inheritdoc} * - * @throws DcGeneralInvalidArgumentException When an a property with the given name has not been registered. + * @throws DcGeneralInvalidArgumentException When a property with the given name has not been registered. */ public function getProperty($name) { diff --git a/src/DataDefinition/Definition/Properties/DefaultProperty.php b/src/DataDefinition/Definition/Properties/DefaultProperty.php index afb82280..316d0b53 100644 --- a/src/DataDefinition/Definition/Properties/DefaultProperty.php +++ b/src/DataDefinition/Definition/Properties/DefaultProperty.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -35,21 +36,21 @@ class DefaultProperty implements PropertyInterface, EmptyValueAwarePropertyInter * * @var string */ - protected $name; + protected $name = ''; /** * The label of the property. * * @var string */ - protected $label; + protected $label = ''; /** * The description of the property. * * @var string */ - protected $description; + protected $description = ''; /** * The default value of the property. @@ -63,60 +64,35 @@ class DefaultProperty implements PropertyInterface, EmptyValueAwarePropertyInter * * @var bool */ - protected $excluded; + protected $excluded = false; /** * Flag if this property shall be searchable. * * @var bool */ - protected $searchable; + protected $searchable = false; /** * Flag if this property shall be sortable. * * @var bool */ - protected $sortable; + protected $sortable = false; /** * Flag if this property shall be filterable. * * @var bool */ - protected $filterable; - - /** - * The grouping mode for this property. - * - * See ListingConfigInterface::GROUP_* flags. - * - * @var string - */ - protected $groupingMode; - - /** - * The grouing length of this property. See grouping mode. - * - * @var string - */ - protected $groupingLength; - - /** - * The sorting mode for this property. - * - * See ListingConfigInterface::SORT_* flags. - * - * @var string - */ - protected $sortingMode; + protected $filterable = false; /** * The input widget type to use. * * @var string */ - protected $widgetType; + protected $widgetType = ''; /** * The value options for this property. @@ -130,7 +106,7 @@ class DefaultProperty implements PropertyInterface, EmptyValueAwarePropertyInter * * @var string */ - protected $explanation; + protected $explanation = ''; /** * The extra information for this property. diff --git a/src/DataDefinition/Definition/PropertiesDefinitionInterface.php b/src/DataDefinition/Definition/PropertiesDefinitionInterface.php index d166d006..825e374a 100644 --- a/src/DataDefinition/Definition/PropertiesDefinitionInterface.php +++ b/src/DataDefinition/Definition/PropertiesDefinitionInterface.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -25,6 +26,8 @@ /** * This interface describes the data definition that holds all property information. + * + * @extends \IteratorAggregate */ interface PropertiesDefinitionInterface extends DefinitionInterface, \IteratorAggregate { diff --git a/src/DataDefinition/Definition/View/Command.php b/src/DataDefinition/Definition/View/Command.php index 2c72c6b3..a275b127 100644 --- a/src/DataDefinition/Definition/View/Command.php +++ b/src/DataDefinition/Definition/View/Command.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -33,7 +34,7 @@ class Command implements CommandInterface * * @var string */ - protected $name; + protected $name = ''; /** * The parameters for the command. @@ -47,14 +48,14 @@ class Command implements CommandInterface * * @var string */ - protected $label; + protected $label = ''; /** * The description text for the command. * * @var string */ - protected $description; + protected $description = ''; /** * The extra data for the command. @@ -68,7 +69,7 @@ class Command implements CommandInterface * * @var bool */ - protected $disabled; + protected $disabled = false; /** * Create a new instance. @@ -84,7 +85,7 @@ public function __construct() */ public function setName($name) { - $this->name = (string) $name; + $this->name = $name; return $this; } @@ -120,7 +121,7 @@ public function getParameters() */ public function setLabel($label) { - $this->label = (string) $label; + $this->label = $label; return $this; } @@ -138,7 +139,7 @@ public function getLabel() */ public function setDescription($description) { - $this->description = (string) $description; + $this->description = $description; return $this; } diff --git a/src/DataDefinition/Definition/View/CommandCollection.php b/src/DataDefinition/Definition/View/CommandCollection.php index 994ba9a8..d1c3a0a4 100644 --- a/src/DataDefinition/Definition/View/CommandCollection.php +++ b/src/DataDefinition/Definition/View/CommandCollection.php @@ -34,7 +34,7 @@ class CommandCollection implements CommandCollectionInterface /** * The commands contained within the collection. * - * @var CommandInterface[] + * @var array */ protected $commands = []; @@ -123,9 +123,9 @@ public function addCommand(CommandInterface $command, CommandInterface $before = $position = \array_search($beforeHash, $hashes); $this->commands = \array_merge( - \array_slice($this->commands, 0, $position), + \array_slice($this->commands, 0, (int) $position), [$hash => $command], - \array_slice($this->commands, $position) + \array_slice($this->commands, (int) $position) ); } else { throw new DcGeneralInvalidArgumentException( diff --git a/src/DataDefinition/Definition/View/CommandCollectionInterface.php b/src/DataDefinition/Definition/View/CommandCollectionInterface.php index b78e9ef5..dd9f2bcb 100644 --- a/src/DataDefinition/Definition/View/CommandCollectionInterface.php +++ b/src/DataDefinition/Definition/View/CommandCollectionInterface.php @@ -124,7 +124,7 @@ public function getCommandNamed($name); /** * Get commands from this collection. * - * @return CommandInterface[]|array + * @return array */ public function getCommands(); } diff --git a/src/DataDefinition/Definition/View/CommandInterface.php b/src/DataDefinition/Definition/View/CommandInterface.php index f0faf407..bf40b27a 100644 --- a/src/DataDefinition/Definition/View/CommandInterface.php +++ b/src/DataDefinition/Definition/View/CommandInterface.php @@ -72,7 +72,7 @@ public function setLabel($label); /** * Return the label of the command. * - * @return array + * @return string */ public function getLabel(); @@ -88,7 +88,7 @@ public function setDescription($description); /** * Return the description of the command. * - * @return array + * @return string */ public function getDescription(); diff --git a/src/DataDefinition/Definition/View/DefaultGroupAndSortingDefinitionCollection.php b/src/DataDefinition/Definition/View/DefaultGroupAndSortingDefinitionCollection.php index 62ee4ae9..57bec044 100644 --- a/src/DataDefinition/Definition/View/DefaultGroupAndSortingDefinitionCollection.php +++ b/src/DataDefinition/Definition/View/DefaultGroupAndSortingDefinitionCollection.php @@ -117,6 +117,8 @@ public function markDefault($information) } $this->default = $information; + + return $this; } /** diff --git a/src/DataDefinition/Definition/View/DefaultGroupAndSortingInformation.php b/src/DataDefinition/Definition/View/DefaultGroupAndSortingInformation.php index 4e2ba6ef..8b136275 100644 --- a/src/DataDefinition/Definition/View/DefaultGroupAndSortingInformation.php +++ b/src/DataDefinition/Definition/View/DefaultGroupAndSortingInformation.php @@ -31,7 +31,7 @@ class DefaultGroupAndSortingInformation implements GroupAndSortingInformationInt * * @var string */ - protected $property; + protected $property = ''; /** * The sorting method to use. @@ -59,7 +59,7 @@ class DefaultGroupAndSortingInformation implements GroupAndSortingInformationInt * * @var bool */ - protected $manualSorting; + protected $manualSorting = false; /** * {@inheritDoc} diff --git a/src/DataDefinition/Definition/View/DefaultListingConfig.php b/src/DataDefinition/Definition/View/DefaultListingConfig.php index cfc5b375..6131ec8d 100644 --- a/src/DataDefinition/Definition/View/DefaultListingConfig.php +++ b/src/DataDefinition/Definition/View/DefaultListingConfig.php @@ -40,11 +40,11 @@ class DefaultListingConfig implements ListingConfigInterface protected $groupAndSorting; /** - * The properties to display in the heder (parented mode only). + * The properties to display in the header (parented mode only). * - * @var array|null + * @var list */ - protected $headerProperties; + protected $headerProperties = []; /** * The root icon to use (hierarchical mode only). @@ -70,7 +70,7 @@ class DefaultListingConfig implements ListingConfigInterface /** * The item formatter to use. * - * @var DefaultModelFormatterConfig[] + * @var array */ protected $itemFormatter = []; diff --git a/src/DataDefinition/Definition/View/DefaultModelFormatterConfig.php b/src/DataDefinition/Definition/View/DefaultModelFormatterConfig.php index 22360968..2e14bdf9 100644 --- a/src/DataDefinition/Definition/View/DefaultModelFormatterConfig.php +++ b/src/DataDefinition/Definition/View/DefaultModelFormatterConfig.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -45,7 +46,7 @@ class DefaultModelFormatterConfig implements ModelFormatterConfigInterface * * @var int|null */ - protected $maxLength; + protected $maxLength = null; /** * {@inheritDoc} @@ -70,7 +71,7 @@ public function getPropertyNames() */ public function setFormat($format) { - $this->format = (string) $format; + $this->format = $format; return $this; } @@ -88,7 +89,7 @@ public function getFormat() */ public function setMaxLength($maxLength) { - $this->maxLength = (null !== $maxLength) ? (int) $maxLength : null; + $this->maxLength = (null !== $maxLength) ? $maxLength : null; return $this; } diff --git a/src/DataDefinition/Definition/View/DefaultPanelRow.php b/src/DataDefinition/Definition/View/DefaultPanelRow.php index 451b28dd..72334906 100644 --- a/src/DataDefinition/Definition/View/DefaultPanelRow.php +++ b/src/DataDefinition/Definition/View/DefaultPanelRow.php @@ -33,7 +33,7 @@ class DefaultPanelRow implements PanelRowInterface /** * The contained elements. * - * @var ElementInformationInterface[] + * @var list */ protected $elements = []; @@ -93,7 +93,7 @@ function ($element) use ($indexOrNameOrInstance) { break; } } - } elseif (\is_numeric($indexOrNameOrInstance)) { + } else { unset($this->elements[$indexOrNameOrInstance]); } @@ -111,18 +111,14 @@ public function hasElement($instanceOrName) return \in_array($instanceOrName, $this->elements); } - if (\is_string($instanceOrName)) { - foreach ($this as $element) { - /** @var ElementInformationInterface $element */ - if ($instanceOrName === $element->getName()) { - return true; - } + foreach ($this as $element) { + /** @var ElementInformationInterface $element */ + if ($instanceOrName === $element->getName()) { + return true; } - - return false; } - throw new DcGeneralInvalidArgumentException('Invalid value for element name given.'); + return false; } /** @@ -148,8 +144,7 @@ public function getElement($indexOrName) return $element; } } - } elseif (!\is_numeric($indexOrName)) { - throw new DcGeneralInvalidArgumentException('Invalid value for element name given.'); + throw new DcGeneralInvalidArgumentException('Value out of bounds: ' . $indexOrName . '.'); } if (!isset($this->elements[$indexOrName])) { diff --git a/src/DataDefinition/Definition/View/DefaultPanelRowCollection.php b/src/DataDefinition/Definition/View/DefaultPanelRowCollection.php index cb1bc81e..1ccfca0a 100644 --- a/src/DataDefinition/Definition/View/DefaultPanelRowCollection.php +++ b/src/DataDefinition/Definition/View/DefaultPanelRowCollection.php @@ -31,7 +31,7 @@ class DefaultPanelRowCollection implements PanelRowCollectionInterface /** * The panel rows. * - * @var PanelRowInterface[] + * @var list */ protected $rows = []; diff --git a/src/DataDefinition/Definition/View/ListingConfigInterface.php b/src/DataDefinition/Definition/View/ListingConfigInterface.php index d7af4a9b..373d381b 100644 --- a/src/DataDefinition/Definition/View/ListingConfigInterface.php +++ b/src/DataDefinition/Definition/View/ListingConfigInterface.php @@ -56,7 +56,7 @@ public function getGroupAndSortingDefinition(); /** * Set the list of parent's model property names. * - * @param array $value The property names to use. + * @param list $value The property names to use. * * @return ListingConfigInterface */ @@ -65,7 +65,7 @@ public function setHeaderPropertyNames($value); /** * Return a list of parent's model property names, which are shown above the item list. * - * @return array + * @return list */ public function getHeaderPropertyNames(); diff --git a/src/DataDefinition/Definition/View/Panel/DefaultFilterElementInformation.php b/src/DataDefinition/Definition/View/Panel/DefaultFilterElementInformation.php index df0b36f1..14e6a8f4 100644 --- a/src/DataDefinition/Definition/View/Panel/DefaultFilterElementInformation.php +++ b/src/DataDefinition/Definition/View/Panel/DefaultFilterElementInformation.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -33,7 +34,7 @@ class DefaultFilterElementInformation implements FilterElementInformationInterfa * * @var string */ - protected $propertyName; + protected $propertyName = ''; /** * {@inheritDoc} @@ -49,6 +50,8 @@ public function getName() public function setPropertyName($propertyName) { $this->propertyName = $propertyName; + + return $this; } /** diff --git a/src/DataDefinition/Definition/View/Panel/DefaultSearchElementInformation.php b/src/DataDefinition/Definition/View/Panel/DefaultSearchElementInformation.php index 6fcf0efc..1324105c 100644 --- a/src/DataDefinition/Definition/View/Panel/DefaultSearchElementInformation.php +++ b/src/DataDefinition/Definition/View/Panel/DefaultSearchElementInformation.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -33,7 +34,7 @@ class DefaultSearchElementInformation implements SearchElementInformationInterfa * * @var array */ - protected $properties; + protected $properties = []; /** * {@inheritDoc} @@ -49,6 +50,8 @@ public function getName() public function addProperty($propertyName) { $this->properties[] = $propertyName; + + return $this; } /** diff --git a/src/DataDefinition/Definition/View/PanelRowCollectionInterface.php b/src/DataDefinition/Definition/View/PanelRowCollectionInterface.php index ec9d33d3..c862b9c0 100644 --- a/src/DataDefinition/Definition/View/PanelRowCollectionInterface.php +++ b/src/DataDefinition/Definition/View/PanelRowCollectionInterface.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -23,6 +24,8 @@ /** * This interface describes a panel row collection. + * + * @extends \IteratorAggregate */ interface PanelRowCollectionInterface extends \IteratorAggregate { @@ -34,7 +37,7 @@ interface PanelRowCollectionInterface extends \IteratorAggregate * * Note that each panel element decides its name on its own. * - * @return array + * @return list> */ public function getRows(); diff --git a/src/DataDefinition/Definition/View/PanelRowInterface.php b/src/DataDefinition/Definition/View/PanelRowInterface.php index b3a75479..96bfcd1e 100644 --- a/src/DataDefinition/Definition/View/PanelRowInterface.php +++ b/src/DataDefinition/Definition/View/PanelRowInterface.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -25,6 +26,8 @@ /** * This interface describes a panel row definition. + * + * @extends \IteratorAggregate */ interface PanelRowInterface extends \IteratorAggregate { @@ -34,7 +37,7 @@ interface PanelRowInterface extends \IteratorAggregate * This will return the following for example: * array('filter[prop1]', 'filter[prop2]', 'search', 'limit') * - * @return array + * @return list */ public function getElements(); diff --git a/src/DataDefinition/Definition/View/ToggleCommand.php b/src/DataDefinition/Definition/View/ToggleCommand.php index d53452be..fb458564 100644 --- a/src/DataDefinition/Definition/View/ToggleCommand.php +++ b/src/DataDefinition/Definition/View/ToggleCommand.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -31,7 +32,7 @@ class ToggleCommand extends Command implements ToggleCommandInterface * * @var string */ - protected $property; + protected $property = ''; /** * The toggle command is an inverse command. @@ -65,7 +66,8 @@ public function getToggleProperty() */ public function setInverse($inverse) { - $this->inverse = (bool) $inverse; + $this->inverse = $inverse; + return $this; } diff --git a/src/DataDefinition/Definition/View/TranslatedToggleCommand.php b/src/DataDefinition/Definition/View/TranslatedToggleCommand.php index f6cb5df8..fa7737f9 100644 --- a/src/DataDefinition/Definition/View/TranslatedToggleCommand.php +++ b/src/DataDefinition/Definition/View/TranslatedToggleCommand.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,8 @@ * @package contao-community-alliance/dc-general * @author Christian Schiffler * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -30,7 +31,7 @@ class TranslatedToggleCommand extends ToggleCommand implements TranslatedToggleC * * @var string */ - protected $language; + protected $language = ''; /** * {@inheritDoc} diff --git a/src/DataDefinition/ModelRelationship/FilterBuilder.php b/src/DataDefinition/ModelRelationship/FilterBuilder.php index 6b1e0ac7..cba1b797 100644 --- a/src/DataDefinition/ModelRelationship/FilterBuilder.php +++ b/src/DataDefinition/ModelRelationship/FilterBuilder.php @@ -60,13 +60,10 @@ class FilterBuilder */ public function __construct($filter = [], $isRoot = false) { - if (!\is_array($filter)) { - throw new DcGeneralInvalidArgumentException( - 'FilterBuilder needs a valid filter array ' . \gettype($filter) . 'given' - ); - } + $filters = static::getBuilderFromArray(['operation' => 'AND', 'children' => $filter], $this); + assert($filters instanceof AndFilterBuilder); - $this->filters = static::getBuilderFromArray(['operation' => 'AND', 'children' => $filter], $this); + $this->filters = $filters; $this->isRootFilter = $isRoot; } @@ -114,6 +111,7 @@ public static function getBuilderFromArray($filter, $builder) */ public static function fromArray($filter = []) { + /** @psalm-suppress UnsafeInstantiation */ return new static($filter, false); } @@ -126,6 +124,7 @@ public static function fromArray($filter = []) */ public static function fromArrayForRoot($filter = []) { + /** @psalm-suppress UnsafeInstantiation */ return new static($filter, true); } diff --git a/src/DataDefinition/ModelRelationship/FilterBuilder/AndFilterBuilder.php b/src/DataDefinition/ModelRelationship/FilterBuilder/AndFilterBuilder.php index 260b04b5..6fa2c56f 100644 --- a/src/DataDefinition/ModelRelationship/FilterBuilder/AndFilterBuilder.php +++ b/src/DataDefinition/ModelRelationship/FilterBuilder/AndFilterBuilder.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,8 @@ * @package contao-community-alliance/dc-general * @author Christian Schiffler * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -33,7 +34,7 @@ class AndFilterBuilder extends FilterBuilderWithChildren /** * Create a new instance. * - * @param array $children The initial children to absorb. + * @param list $children The initial children to absorb. * * @throws DcGeneralInvalidArgumentException When invalid children have been passed. */ @@ -58,7 +59,7 @@ public function append($filters) } if ($filters instanceof AndFilterBuilder) { - foreach (clone $filters as $filter) { + foreach ($filters as $filter) { $this->add(clone $filter); } diff --git a/src/DataDefinition/ModelRelationship/FilterBuilder/BaseComparingFilterBuilder.php b/src/DataDefinition/ModelRelationship/FilterBuilder/BaseComparingFilterBuilder.php index 2472c7a5..24fb91a0 100644 --- a/src/DataDefinition/ModelRelationship/FilterBuilder/BaseComparingFilterBuilder.php +++ b/src/DataDefinition/ModelRelationship/FilterBuilder/BaseComparingFilterBuilder.php @@ -35,42 +35,35 @@ class BaseComparingFilterBuilder extends BaseFilterBuilder * * @var string */ - protected $operation; + protected $operation = ''; /** * The property to be checked. * * @var string */ - protected $property; - - /** - * The property to be checked. - * - * @var string - */ - protected $remoteProperty; + protected $property = ''; /** * The value to compare against. * * @var mixed */ - protected $value; + protected $value = ''; /** * Flag determining if the passed value is a remote property name or not. * * @var bool */ - protected $isRemote; + protected $isRemote = false; /** * Flag determining if the remote value is a property or literal value. * * @var bool */ - protected $isRemoteProp; + protected $isRemoteProp = false; /** * Create a new instance. @@ -124,6 +117,7 @@ public static function fromArray($array) throw new DcGeneralInvalidArgumentException('Invalid filter array provided ' . \var_export($array, true)); } + /** @psalm-suppress UnsafeInstantiation */ return new static($property, $value, $isRemote, $isRemoteProp); } diff --git a/src/DataDefinition/ModelRelationship/FilterBuilder/BaseFilterBuilder.php b/src/DataDefinition/ModelRelationship/FilterBuilder/BaseFilterBuilder.php index bb6f7763..3575259d 100644 --- a/src/DataDefinition/ModelRelationship/FilterBuilder/BaseFilterBuilder.php +++ b/src/DataDefinition/ModelRelationship/FilterBuilder/BaseFilterBuilder.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Stefan Heimes * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -33,16 +34,16 @@ abstract class BaseFilterBuilder /** * The filter builder holding the scope. * - * @var FilterBuilder + * @var null|FilterBuilder */ - protected $builder; + protected $builder = null; /** - * The current parenting Builder. + * The current parenting builder. * - * @var FilterBuilderWithChildren + * @var null|FilterBuilderWithChildren */ - protected $parent; + protected $parent = null; /** * Get the filter builder. @@ -51,6 +52,10 @@ abstract class BaseFilterBuilder */ public function getBuilder() { + if (null === $this->builder) { + throw new \LogicException('Builder has not been set.'); + } + return $this->builder; } @@ -73,7 +78,7 @@ public function setBuilder($builder) * * @param FilterBuilderWithChildren $parent The new parent. * - * @return FilterBuilderWithChildren + * @return BaseFilterBuilder */ public function setParent(FilterBuilderWithChildren $parent) { @@ -94,6 +99,10 @@ public function setParent(FilterBuilderWithChildren $parent) */ public function getParent() { + if (null === $this->parent) { + throw new \LogicException('Parent has not been set.'); + } + return $this->parent; } @@ -111,7 +120,10 @@ abstract public function get(); */ public function getAllAsArray() { - return $this->builder->getAllAsArray(); + $builder = $this->builder; + assert($builder instanceof FilterBuilder); + + return $builder->getAllAsArray(); } /** @@ -121,12 +133,11 @@ public function getAllAsArray() */ protected function ensureAndEncapsulation() { - $parent = $this->getParent(); - if ($this instanceof AndFilterBuilder) { return $this; } + $parent = $this->getParent(); if (($parent instanceof AndFilterBuilder) && !($this instanceof FilterBuilderWithChildren)) { return $parent; } @@ -227,7 +238,7 @@ public function up() * @param string $property The property name. * @param mixed $value The property value. * - * @return PropertyEqualsFilterBuilder The newly created filter. + * @return BaseFilterBuilder The newly created filter. */ public function andPropertyEquals($property, $value) { @@ -240,7 +251,7 @@ public function andPropertyEquals($property, $value) * @param string $property The property name. * @param mixed $value The property value. * - * @return PropertyEqualsFilterBuilder The newly created filter. + * @return BaseFilterBuilder The newly created filter. */ public function orPropertyEquals($property, $value) { @@ -254,7 +265,7 @@ public function orPropertyEquals($property, $value) * @param string $remoteProperty The name of the remote property. * @param bool $remoteIsValue True if the passed remote value is a value, false if it is a property name. * - * @return PropertyEqualsFilterBuilder The newly created filter. + * @return BaseFilterBuilder The newly created filter. */ public function andRemotePropertyEquals($property, $remoteProperty, $remoteIsValue = false) { @@ -271,7 +282,7 @@ public function andRemotePropertyEquals($property, $remoteProperty, $remoteIsVal * @param string $property The property name. * @param mixed $value The property value. * - * @return PropertyGreaterThanFilterBuilder The newly created filter. + * @return BaseFilterBuilder The newly created filter. */ public function andPropertyGreaterThan($property, $value) { @@ -285,7 +296,7 @@ public function andPropertyGreaterThan($property, $value) * @param string $remoteProperty The name of the remote property. * @param bool $remoteIsValue True if the passed remote value is a value, false if it is a property name. * - * @return PropertyGreaterThanFilterBuilder The newly created filter. + * @return BaseFilterBuilder The newly created filter. */ public function andRemotePropertyGreaterThan($property, $remoteProperty, $remoteIsValue = false) { @@ -302,7 +313,7 @@ public function andRemotePropertyGreaterThan($property, $remoteProperty, $remote * @param string $property The property name. * @param mixed $value The property value. * - * @return PropertyLessThanFilterBuilder The newly created filter. + * @return BaseFilterBuilder The newly created filter. */ public function andPropertyLessThan($property, $value) { @@ -316,7 +327,7 @@ public function andPropertyLessThan($property, $value) * @param string $remoteProperty The name of the remote property. * @param bool $remoteIsValue True if the passed remote value is a value, false if it is a property name. * - * @return PropertyLessThanFilterBuilder The newly created filter. + * @return BaseFilterBuilder The newly created filter. */ public function andRemotePropertyLessThan($property, $remoteProperty, $remoteIsValue = false) { @@ -333,7 +344,7 @@ public function andRemotePropertyLessThan($property, $remoteProperty, $remoteIsV * @param string $property The property name. * @param mixed $value The property value. * - * @return PropertyValueInFilterBuilder The newly created filter. + * @return BaseFilterBuilder The newly created filter. */ public function andPropertyValueIn($property, $value) { @@ -346,7 +357,7 @@ public function andPropertyValueIn($property, $value) * @param string $property The property name. * @param mixed $value The property value. * - * @return PropertyValueInFilterBuilder The newly created filter. + * @return BaseFilterBuilder The newly created filter. */ public function andPropertyValueLike($property, $value) { diff --git a/src/DataDefinition/ModelRelationship/FilterBuilder/FilterBuilderWithChildren.php b/src/DataDefinition/ModelRelationship/FilterBuilder/FilterBuilderWithChildren.php index 47d364ec..ca0bd63f 100644 --- a/src/DataDefinition/ModelRelationship/FilterBuilder/FilterBuilderWithChildren.php +++ b/src/DataDefinition/ModelRelationship/FilterBuilder/FilterBuilderWithChildren.php @@ -21,30 +21,38 @@ namespace ContaoCommunityAlliance\DcGeneral\DataDefinition\ModelRelationship\FilterBuilder; +use ArrayAccess; use ContaoCommunityAlliance\DcGeneral\DataDefinition\ModelRelationship\FilterBuilder; use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralInvalidArgumentException; use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralRuntimeException; +use Iterator; +use ReturnTypeWillChange; + +use function count; /** * Handy helper class to generate and manipulate filter arrays containing children. * * This class is intended to be only used as base class of other filters and via the FilterBuilder main class. * + * @implements Iterator + * @implements ArrayAccess + * * @SuppressWarnings(PHPMD.TooManyPublicMethods) We have to keep them as we implement the interfaces. */ -class FilterBuilderWithChildren extends BaseFilterBuilder implements \Iterator, \ArrayAccess +class FilterBuilderWithChildren extends BaseFilterBuilder implements Iterator, ArrayAccess { /** * The operation string. * * @var string */ - protected $operation; + protected $operation = ''; /** * The children within this filter builder. * - * @var BaseFilterBuilder[] + * @var array */ protected $children; @@ -58,18 +66,12 @@ class FilterBuilderWithChildren extends BaseFilterBuilder implements \Iterator, /** * Create a new instance. * - * @param array $children The initial children to absorb. + * @param list $children The initial children to absorb. * * @throws DcGeneralInvalidArgumentException When invalid children have been passed. */ public function __construct($children = []) { - if (!\is_array($children)) { - throw new DcGeneralInvalidArgumentException( - __CLASS__ . ' needs a valid child filter array ' . \gettype($children) . 'given' - ); - } - $this->children = []; foreach ($children as $child) { @@ -79,7 +81,7 @@ public function __construct($children = []) } /** - * {@inheritdoc} + * Clone the object. */ public function __clone() { @@ -98,10 +100,10 @@ public function __clone() * * @throws DcGeneralRuntimeException When the current position is invalid. */ - public function current(): ?BaseFilterBuilder + public function current(): BaseFilterBuilder { if (-1 === $this->index) { - return $this->first(); + $this->first(); } if (!$this->valid()) { @@ -114,8 +116,11 @@ public function current(): ?BaseFilterBuilder /** * Move forward to next element and return it. * - * @return BaseFilterBuilder + * @return BaseFilterBuilder|null + * + * @psalm-suppress ImplementedReturnTypeMismatch */ + #[ReturnTypeWillChange] public function next(): ?BaseFilterBuilder { $this->index++; @@ -126,9 +131,9 @@ public function next(): ?BaseFilterBuilder /** * Return the key of the current element. * - * @return mixed scalar on success, or null on failure. + * @return int scalar on success, or null on failure. */ - public function key(): mixed + public function key(): int { return $this->index; } @@ -140,7 +145,7 @@ public function key(): mixed */ public function valid(): bool { - return ($this->index > -1) && ($this->index < \count($this->children)); + return ($this->index > -1) && ($this->index < count($this->children)); } /** @@ -148,8 +153,11 @@ public function valid(): bool * * This is an alias for {@link FilterBuilderWithChildren::first()} only present for implementing Iterator interface. * - * @return BaseFilterBuilder + * @return BaseFilterBuilder|null + * + * @psalm-suppress ImplementedReturnTypeMismatch */ + #[ReturnTypeWillChange] public function rewind(): ?BaseFilterBuilder { return $this->first(); @@ -158,11 +166,11 @@ public function rewind(): ?BaseFilterBuilder /** * Rewind the Iterator to the first element and return it. * - * @return BaseFilterBuilder + * @return BaseFilterBuilder|null */ public function first() { - $this->index = \count($this->children) ? 0 : (-1); + $this->index = count($this->children) ? 0 : (-1); if ($this->index === -1) { return null; @@ -202,7 +210,10 @@ public function offsetGet($offset): BaseFilterBuilder * @param mixed $value The value to set. * * @return FilterBuilderWithChildren The current builder. + * + * @psalm-suppress ImplementedReturnTypeMismatch */ + #[ReturnTypeWillChange] public function offsetSet($offset, $value): FilterBuilderWithChildren { $this->children[$offset] = $value; @@ -216,7 +227,10 @@ public function offsetSet($offset, $value): FilterBuilderWithChildren * @param int $offset The offset to unset. * * @return FilterBuilderWithChildren The current builder. + * + * @psalm-suppress ImplementedReturnTypeMismatch */ + #[ReturnTypeWillChange] public function offsetUnset($offset): FilterBuilderWithChildren { unset($this->children[$offset]); @@ -269,7 +283,10 @@ public function add($filter) if ($index === -1) { $this->children[] = $filter; - $filter->setBuilder($this->builder)->setParent($this); + $filter->setParent($this); + if (null !== $this->builder) { + $filter->setBuilder($this->builder); + } } return $this; @@ -308,6 +325,7 @@ public static function fromArray($array, $builder) $children[] = FilterBuilder::getBuilderFromArray($child, $builder); } + /** @psalm-suppress UnsafeInstantiation */ return (new static($children))->setBuilder($builder); } diff --git a/src/DataDefinition/ModelRelationship/FilterBuilder/OrFilterBuilder.php b/src/DataDefinition/ModelRelationship/FilterBuilder/OrFilterBuilder.php index de427cd7..0a2940f4 100644 --- a/src/DataDefinition/ModelRelationship/FilterBuilder/OrFilterBuilder.php +++ b/src/DataDefinition/ModelRelationship/FilterBuilder/OrFilterBuilder.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,8 @@ * @package contao-community-alliance/dc-general * @author Christian Schiffler * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -32,7 +33,7 @@ class OrFilterBuilder extends FilterBuilderWithChildren /** * Create a new instance. * - * @param array $children The initial children to absorb. + * @param list $children The initial children to absorb. * * @throws DcGeneralInvalidArgumentException When invalid children have been passed. */ diff --git a/src/DataDefinition/ModelRelationship/FilterBuilder/PropertyValueInFilterBuilder.php b/src/DataDefinition/ModelRelationship/FilterBuilder/PropertyValueInFilterBuilder.php index 4233b42e..fd3341b1 100644 --- a/src/DataDefinition/ModelRelationship/FilterBuilder/PropertyValueInFilterBuilder.php +++ b/src/DataDefinition/ModelRelationship/FilterBuilder/PropertyValueInFilterBuilder.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,8 @@ * @package contao-community-alliance/dc-general * @author Christian Schiffler * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -51,7 +52,6 @@ class PropertyValueInFilterBuilder extends BaseFilterBuilder */ public function __construct($property, $values) { - $this->operation = 'IN'; $this->setProperty($property)->setValues($values); } @@ -60,7 +60,7 @@ public function __construct($property, $values) * * @param array $array The initialization array. * - * @return mixed + * @return static * * @throws DcGeneralInvalidArgumentException When an invalid array has been passed. */ @@ -73,6 +73,7 @@ public static function fromArray($array) throw new DcGeneralInvalidArgumentException('Invalid filter array provided ' . \var_export($array, true)); } + /** @psalm-suppress UnsafeInstantiation */ return new static($property, $values); } diff --git a/src/DataDefinition/ModelRelationship/ParentChildCondition.php b/src/DataDefinition/ModelRelationship/ParentChildCondition.php index 0acec112..72ddd84f 100644 --- a/src/DataDefinition/ModelRelationship/ParentChildCondition.php +++ b/src/DataDefinition/ModelRelationship/ParentChildCondition.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2021 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -16,7 +16,8 @@ * @author Tristan Lins * @author Sven Baumann * @author David Molineus - * @copyright 2013-2021 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -46,43 +47,43 @@ class ParentChildCondition extends AbstractCondition implements ParentChildCondi * * @var array */ - protected $inverseFilter; + protected $inverseFilter = []; /** * The values to use when enforcing a root condition. * * @var array */ - protected $setOn; + protected $setOn = []; /** * The name of the source provider (parent). * * @var string */ - protected $sourceProvider; + protected $sourceProvider = ''; /** * The name of the destination provider (child). * * @var string */ - protected $destinationProvider; + protected $destinationProvider = ''; /** * Local cache property for the needed properties for filtering. * - * @var array + * @var list|null */ - private $neededProperties; + private $neededProperties = null; /** * {@inheritdoc} */ public function setFilterArray($value) { - $this->filter = (array) $value; - unset($this->neededProperties); + $this->filter = $value; + $this->neededProperties = null; return $this; } @@ -100,7 +101,7 @@ public function getFilterArray() */ public function setSetters($value) { - $this->setOn = (array) $value; + $this->setOn = $value; return $this; } @@ -118,12 +119,8 @@ public function getSetters() */ public function setInverseFilterArray($value) { - if (empty($value)) { - $this->inverseFilter = null; - return $this; - } + $this->inverseFilter = $value; - $this->inverseFilter = (array) $value; return $this; } @@ -132,7 +129,7 @@ public function setInverseFilterArray($value) */ public function getInverseFilterArray() { - return ($this->inverseFilter ?? []); + return $this->inverseFilter; } /** @@ -140,7 +137,7 @@ public function getInverseFilterArray() */ public function setSourceName($value) { - $this->sourceProvider = (string) $value; + $this->sourceProvider = $value; return $this; } @@ -158,7 +155,7 @@ public function getSourceName() */ public function setDestinationName($value) { - $this->destinationProvider = (string) $value; + $this->destinationProvider = $value; return $this; } @@ -219,10 +216,6 @@ public function parseFilter($filter, $model) */ public function getFilter($parent) { - if (!$parent) { - throw new DcGeneralInvalidArgumentException('No parent model passed.'); - } - $result = []; foreach ($this->getFilterArray() as $child) { $result[] = $this->parseFilter($child, $parent); @@ -234,7 +227,7 @@ public function getFilter($parent) /** * Check if the passed value is a valid setter. * - * @param array $setter The setter. + * @param mixed $setter The setter. * * @return bool */ @@ -257,7 +250,7 @@ public function applyTo($objParent, $objChild) $setters = $this->getSetters(); - if (empty($setters) || !\is_array($setters)) { + if (empty($setters)) { throw new DcGeneralRuntimeException( \sprintf( 'No relationship setter defined from %s to %s.', @@ -300,7 +293,7 @@ public function copyFrom($sourceModel, $destinationModel) $setters = $this->getSetters(); - if (empty($setters) || !\is_array($setters)) { + if (empty($setters)) { throw new DcGeneralRuntimeException( \sprintf( 'No relationship setter defined from %s to %s.', @@ -372,7 +365,7 @@ public function getInverseFilterFor($child) * @param array $rule The rule to prepare. * @param ModelInterface $child The child to be checked. * - * @return array. + * @return array */ protected function prepareRule($rule, $child) { @@ -436,28 +429,31 @@ public function matches($objParent, $objChild) * * @param array $rule The filter rule from which the properties shall be extracted from. * - * @return array + * @return list * * @throws \RuntimeException When an unexpected filter rule is encountered. */ private function extractNeededProperties($rule) { if (\in_array($rule['operation'], ['AND', 'OR'])) { - $properties = []; - foreach ($rule['children'] as $childRule) { + $properties = [[]]; + foreach ($rule['children'] ?? [] as $childRule) { $properties[] = $this->extractNeededProperties($childRule); } - return $properties; + + return \array_merge(...$properties); } // Local is child property name. if (isset($rule['local'])) { - return $rule['local']; + /** @var array{local: string} $rule */ + return [$rule['local']]; } // Remote is parent property name. if (isset($rule['property'])) { - return $rule['property']; + /** @var array{property: string} $rule */ + return [$rule['property']]; } throw new \RuntimeException('Unexpected filter rule ' . \var_export($rule, true)); @@ -468,7 +464,7 @@ private function extractNeededProperties($rule) */ public function neededProperties() { - if (!isset($this->neededProperties)) { + if (null === $this->neededProperties) { $this->neededProperties = $this->extractNeededProperties( [ 'operation' => 'AND', diff --git a/src/DataDefinition/ModelRelationship/ParentChildConditionInterface.php b/src/DataDefinition/ModelRelationship/ParentChildConditionInterface.php index d3d48538..62c07fc0 100644 --- a/src/DataDefinition/ModelRelationship/ParentChildConditionInterface.php +++ b/src/DataDefinition/ModelRelationship/ParentChildConditionInterface.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -79,11 +80,11 @@ public function getInverseFilterArray(); /** * Get the condition as filter related to the given model. * - * @param ModelInterface $objParent The model that shall get used as parent. + * @param ModelInterface $parent The model that shall get used as parent. * * @return array */ - public function getFilter($objParent); + public function getFilter($parent); /** * Set the name of the source provider. @@ -142,12 +143,12 @@ public function copyFrom($sourceModel, $destinationModel); * * This allows to look up the parent of a child model. * - * @param ModelInterface $objChild The model that shall get used as child and for which the parent filter shall get - * retrieved. + * @param ModelInterface $child The model that shall get used as child and for which the parent filter shall get + * retrieved. * * @return array|null */ - public function getInverseFilterFor($objChild); + public function getInverseFilterFor($child); /** * Test if the given parent is indeed a parent of the given child object for this condition. @@ -162,7 +163,7 @@ public function matches($objParent, $objChild); /** * Return the names of the needed properties for filtering. * - * @return array + * @return list */ public function neededProperties(); } diff --git a/src/DataDefinition/ModelRelationship/RootCondition.php b/src/DataDefinition/ModelRelationship/RootCondition.php index 46fe8459..1e8df211 100644 --- a/src/DataDefinition/ModelRelationship/RootCondition.php +++ b/src/DataDefinition/ModelRelationship/RootCondition.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Sven Baumann * @author Richard Henkenjohann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -34,7 +35,7 @@ class RootCondition extends AbstractCondition implements RootConditionInterface * * @var array */ - protected $filter; + protected $filter = []; /** * The setter information to use when a model shall get marked as root item. @@ -48,7 +49,7 @@ class RootCondition extends AbstractCondition implements RootConditionInterface * * @var string */ - protected $sourceProvider; + protected $sourceProvider = ''; /** * {@inheritdoc} @@ -140,17 +141,17 @@ public function applyTo($model) /** * {@inheritdoc} */ - public function matches($model) + public function matches($objModel) { try { - $this->guardProviderName($model); + $this->guardProviderName($objModel); } catch (\InvalidArgumentException $exception) { return false; } if ($this->getFilterArray()) { return static::checkCondition( - $model, + $objModel, [ 'operation' => 'AND', 'children' => $this->getFilterArray() diff --git a/src/DataDefinition/ModelRelationship/RootConditionInterface.php b/src/DataDefinition/ModelRelationship/RootConditionInterface.php index 1aedace3..67c07fc2 100644 --- a/src/DataDefinition/ModelRelationship/RootConditionInterface.php +++ b/src/DataDefinition/ModelRelationship/RootConditionInterface.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,7 @@ * @package contao-community-alliance/dc-general * @author Christian Schiffler * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -78,11 +78,11 @@ public function getSourceName(); /** * Apply the root condition to a model. * - * @param ModelInterface $objModel The model that shall become a root model. + * @param ModelInterface $model The model that shall become a root model. * * @return RootConditionInterface */ - public function applyTo($objModel); + public function applyTo($model); /** * Test if the given model is indeed a root object according to this condition. diff --git a/src/DataDefinition/Palette/Builder/Event/AddConditionEvent.php b/src/DataDefinition/Palette/Builder/Event/AddConditionEvent.php index 28509af2..338eca0d 100644 --- a/src/DataDefinition/Palette/Builder/Event/AddConditionEvent.php +++ b/src/DataDefinition/Palette/Builder/Event/AddConditionEvent.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -30,6 +31,9 @@ /** * This event is emitted when an condition is added by the palette builder. + * + * @template TCondition of PaletteConditionInterface|PropertyConditionInterface + * @template TTarget of PaletteInterface|PropertyInterface */ class AddConditionEvent extends BuilderEvent { @@ -38,23 +42,23 @@ class AddConditionEvent extends BuilderEvent /** * The condition that is being added. * - * @var PaletteConditionInterface|PropertyConditionInterface + * @var TCondition */ protected $condition; /** * The target to which the condition is being added. * - * @var PaletteInterface|PropertyInterface + * @var TTarget */ protected $target; /** * Create a new instance. * - * @param PaletteConditionInterface|PropertyConditionInterface $condition The condition being added. - * @param PaletteInterface|PropertyInterface $target The target property or palette. - * @param PaletteBuilder $paletteBuilder The palette builder in use. + * @param TCondition $condition The condition being added. + * @param TTarget $target The target property or palette. + * @param PaletteBuilder $paletteBuilder The palette builder in use. */ public function __construct($condition, $target, PaletteBuilder $paletteBuilder) { @@ -66,7 +70,7 @@ public function __construct($condition, $target, PaletteBuilder $paletteBuilder) /** * Set the condition. * - * @param PaletteConditionInterface|PropertyConditionInterface $condition The condition. + * @param TCondition $condition The condition. * * @return AddConditionEvent * @@ -91,7 +95,7 @@ public function setCondition($condition) /** * Retrieve the condition. * - * @return PaletteConditionInterface|PropertyConditionInterface + * @return TCondition */ public function getCondition() { @@ -101,7 +105,7 @@ public function getCondition() /** * Set the target. * - * @param PaletteInterface|PropertyInterface $target The target property or palette. + * @param TTarget $target The target property or palette. * * @return AddConditionEvent * @@ -120,7 +124,7 @@ public function setTarget($target) /** * Retrieve the target. * - * @return PaletteInterface|PropertyInterface + * @return TTarget */ public function getTarget() { diff --git a/src/DataDefinition/Palette/Builder/Event/BuilderEvent.php b/src/DataDefinition/Palette/Builder/Event/BuilderEvent.php index de13da7e..bbaee38a 100644 --- a/src/DataDefinition/Palette/Builder/Event/BuilderEvent.php +++ b/src/DataDefinition/Palette/Builder/Event/BuilderEvent.php @@ -32,6 +32,9 @@ */ abstract class BuilderEvent extends AbstractContainerAwareEvent { + /** + * @var string + */ public const NAME = 'dc-general.data-definition.palette.builder.builder'; /** @@ -48,7 +51,7 @@ abstract class BuilderEvent extends AbstractContainerAwareEvent */ public function __construct(PaletteBuilder $paletteBuilder) { - parent::__construct($this->paletteBuilder->getContainer()); + parent::__construct($paletteBuilder->getContainer()); $this->paletteBuilder = $paletteBuilder; } diff --git a/src/DataDefinition/Palette/Builder/Event/CreateConditionEvent.php b/src/DataDefinition/Palette/Builder/Event/CreateConditionEvent.php index 0c2b5ef0..a4a4175c 100644 --- a/src/DataDefinition/Palette/Builder/Event/CreateConditionEvent.php +++ b/src/DataDefinition/Palette/Builder/Event/CreateConditionEvent.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -28,6 +29,8 @@ /** * This event gets emitted when a condition is created. + * + * @template TCondition of PaletteConditionInterface|PropertyConditionInterface */ class CreateConditionEvent extends BuilderEvent { @@ -36,16 +39,16 @@ class CreateConditionEvent extends BuilderEvent /** * The condition being created. * - * @var PaletteConditionInterface|PropertyConditionInterface + * @var TCondition */ protected $condition; /** * Create a new instance. * - * @param PaletteConditionInterface|PropertyConditionInterface $condition The condition that has been created. - * @param PaletteBuilder $paletteBuilder The palette builder that created the - * condition. + * @param TCondition $condition The condition that has been created. + * @param PaletteBuilder $paletteBuilder The palette builder that created the + * condition. */ public function __construct($condition, PaletteBuilder $paletteBuilder) { @@ -57,7 +60,7 @@ public function __construct($condition, PaletteBuilder $paletteBuilder) /** * Set the condition. * - * @param PaletteConditionInterface|PropertyConditionInterface $condition The condition to use. + * @param TCondition $condition The condition to use. * * @return CreateConditionEvent * @@ -83,7 +86,7 @@ public function setCondition($condition) /** * Retrieve the condition. * - * @return PaletteConditionInterface|PropertyConditionInterface + * @return TCondition */ public function getCondition() { diff --git a/src/DataDefinition/Palette/Builder/Event/CreateDefaultPaletteConditionEvent.php b/src/DataDefinition/Palette/Builder/Event/CreateDefaultPaletteConditionEvent.php index 08b47943..275ba3fc 100644 --- a/src/DataDefinition/Palette/Builder/Event/CreateDefaultPaletteConditionEvent.php +++ b/src/DataDefinition/Palette/Builder/Event/CreateDefaultPaletteConditionEvent.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -22,7 +23,7 @@ namespace ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Builder\Event; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Builder\PaletteBuilder; -use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Condition\Palette\DefaultPaletteCondition; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Condition\Palette\PaletteConditionInterface; /** * This event gets emitted when a condition for the default palette is created. @@ -34,17 +35,17 @@ class CreateDefaultPaletteConditionEvent extends BuilderEvent /** * The default palette condition. * - * @var DefaultPaletteCondition + * @var PaletteConditionInterface */ protected $paletteCondition; /** * Create a new instance. * - * @param DefaultPaletteCondition $paletteCondition The condition that has been created. - * @param PaletteBuilder $paletteBuilder The palette builder creating the condition. + * @param PaletteConditionInterface $paletteCondition The condition that has been created. + * @param PaletteBuilder $paletteBuilder The palette builder creating the condition. */ - public function __construct(DefaultPaletteCondition $paletteCondition, PaletteBuilder $paletteBuilder) + public function __construct(PaletteConditionInterface $paletteCondition, PaletteBuilder $paletteBuilder) { $this->setDefaultPaletteCondition($paletteCondition); parent::__construct($paletteBuilder); @@ -53,11 +54,11 @@ public function __construct(DefaultPaletteCondition $paletteCondition, PaletteBu /** * Set the condition. * - * @param DefaultPaletteCondition $paletteCondition The condition. + * @param PaletteConditionInterface $paletteCondition The condition. * * @return CreateDefaultPaletteConditionEvent */ - public function setDefaultPaletteCondition(DefaultPaletteCondition $paletteCondition) + public function setDefaultPaletteCondition(PaletteConditionInterface $paletteCondition) { $this->paletteCondition = $paletteCondition; @@ -67,7 +68,7 @@ public function setDefaultPaletteCondition(DefaultPaletteCondition $paletteCondi /** * Retrieve the condition. * - * @return DefaultPaletteCondition + * @return PaletteConditionInterface */ public function getDefaultPaletteCondition() { diff --git a/src/DataDefinition/Palette/Builder/Event/CreatePaletteConditionChainEvent.php b/src/DataDefinition/Palette/Builder/Event/CreatePaletteConditionChainEvent.php index 0626ff95..5b50a4f3 100644 --- a/src/DataDefinition/Palette/Builder/Event/CreatePaletteConditionChainEvent.php +++ b/src/DataDefinition/Palette/Builder/Event/CreatePaletteConditionChainEvent.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,15 +14,17 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ namespace ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Builder\Event; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ConditionChainInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Builder\PaletteBuilder; -use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Condition\Palette\PaletteConditionChain; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Condition\Palette\PaletteConditionInterface; /** * This event gets emitted when a palette condition chain is created. @@ -34,18 +36,20 @@ class CreatePaletteConditionChainEvent extends BuilderEvent /** * The palette condition chain being created. * - * @var PaletteConditionChain + * @var ConditionChainInterface&PaletteConditionInterface */ protected $conditionChain; /** * Create a new instance. * - * @param PaletteConditionChain $conditionChain The palette condition chain. - * @param PaletteBuilder $paletteBuilder The palette builder in use. + * @param ConditionChainInterface&PaletteConditionInterface $conditionChain The palette condition chain. + * @param PaletteBuilder $paletteBuilder The palette builder in use. */ - public function __construct(PaletteConditionChain $conditionChain, PaletteBuilder $paletteBuilder) - { + public function __construct( + ConditionChainInterface&PaletteConditionInterface $conditionChain, + PaletteBuilder $paletteBuilder + ) { $this->setPaletteConditionChain($conditionChain); parent::__construct($paletteBuilder); } @@ -53,11 +57,11 @@ public function __construct(PaletteConditionChain $conditionChain, PaletteBuilde /** * Set the palette condition chain. * - * @param PaletteConditionChain $conditionChain The condition chain. + * @param ConditionChainInterface&PaletteConditionInterface $conditionChain The condition chain. * * @return CreatePaletteConditionChainEvent */ - public function setPaletteConditionChain(PaletteConditionChain $conditionChain) + public function setPaletteConditionChain(ConditionChainInterface&PaletteConditionInterface $conditionChain) { $this->conditionChain = $conditionChain; @@ -67,7 +71,7 @@ public function setPaletteConditionChain(PaletteConditionChain $conditionChain) /** * Retrieve the palette condition chain. * - * @return PaletteConditionChain + * @return ConditionChainInterface&PaletteConditionInterface */ public function getPaletteConditionChain() { diff --git a/src/DataDefinition/Palette/Builder/Event/CreatePropertyConditionChainEvent.php b/src/DataDefinition/Palette/Builder/Event/CreatePropertyConditionChainEvent.php index 2fdfdac5..41a55041 100644 --- a/src/DataDefinition/Palette/Builder/Event/CreatePropertyConditionChainEvent.php +++ b/src/DataDefinition/Palette/Builder/Event/CreatePropertyConditionChainEvent.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,15 +14,17 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ namespace ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Builder\Event; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ConditionChainInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Builder\PaletteBuilder; -use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Condition\Property\PropertyConditionChain; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Condition\Property\PropertyConditionInterface; /** * This event gets emitted when a property condition chain is created. @@ -34,18 +36,21 @@ class CreatePropertyConditionChainEvent extends BuilderEvent /** * The property condition chain. * - * @var PropertyConditionChain + * @var ConditionChainInterface&PropertyConditionInterface */ protected $conditionChain; /** * Create a new instance. * - * @param PropertyConditionChain $conditionChain The property condition chain that has been created. - * @param PaletteBuilder $paletteBuilder The palette builder in use. + * @param ConditionChainInterface&PropertyConditionInterface $conditionChain The property condition chain that has + * been created. + * @param PaletteBuilder $paletteBuilder The palette builder in use. */ - public function __construct(PropertyConditionChain $conditionChain, PaletteBuilder $paletteBuilder) - { + public function __construct( + ConditionChainInterface&PropertyConditionInterface $conditionChain, + PaletteBuilder $paletteBuilder + ) { $this->setPropertyConditionChain($conditionChain); parent::__construct($paletteBuilder); } @@ -53,11 +58,11 @@ public function __construct(PropertyConditionChain $conditionChain, PaletteBuild /** * Set the property condition chain. * - * @param PropertyConditionChain $conditionChain The property condition chain. + * @param ConditionChainInterface&PropertyConditionInterface $conditionChain The property condition chain. * * @return CreatePropertyConditionChainEvent */ - public function setPropertyConditionChain(PropertyConditionChain $conditionChain) + public function setPropertyConditionChain(ConditionChainInterface&PropertyConditionInterface $conditionChain) { $this->conditionChain = $conditionChain; return $this; @@ -66,7 +71,7 @@ public function setPropertyConditionChain(PropertyConditionChain $conditionChain /** * Retrieve the property condition chain. * - * @return PropertyConditionChain + * @return ConditionChainInterface&PropertyConditionInterface */ public function getPropertyConditionChain() { diff --git a/src/DataDefinition/Palette/Builder/Event/SetDefaultPaletteConditionClassNameEvent.php b/src/DataDefinition/Palette/Builder/Event/SetDefaultPaletteConditionClassNameEvent.php index 6291f5d9..2bddd9e3 100644 --- a/src/DataDefinition/Palette/Builder/Event/SetDefaultPaletteConditionClassNameEvent.php +++ b/src/DataDefinition/Palette/Builder/Event/SetDefaultPaletteConditionClassNameEvent.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -22,6 +23,7 @@ namespace ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Builder\Event; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Builder\PaletteBuilder; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Condition\Palette\PaletteConditionInterface; /** * This event gets emitted when the class name of the default palette condition is set. @@ -33,15 +35,15 @@ class SetDefaultPaletteConditionClassNameEvent extends BuilderEvent /** * The class name. * - * @var string + * @var class-string */ protected $className; /** * Create a new instance. * - * @param string $className The class name. - * @param PaletteBuilder $paletteBuilder The palette builder in use. + * @param class-string $className The class name. + * @param PaletteBuilder $paletteBuilder The palette builder in use. */ public function __construct($className, PaletteBuilder $paletteBuilder) { @@ -52,20 +54,20 @@ public function __construct($className, PaletteBuilder $paletteBuilder) /** * Set the class name. * - * @param string $className The class name. + * @param class-string $className The class name. * * @return SetDefaultPaletteConditionClassNameEvent */ public function setDefaultPaletteConditionClassName($className) { - $this->className = (string) $className; + $this->className = $className; return $this; } /** * Retrieve the class name. * - * @return string + * @return class-string */ public function getDefaultPaletteConditionClassName() { diff --git a/src/DataDefinition/Palette/Builder/Event/SetLegendClassNameEvent.php b/src/DataDefinition/Palette/Builder/Event/SetLegendClassNameEvent.php index b18d3c53..520216da 100644 --- a/src/DataDefinition/Palette/Builder/Event/SetLegendClassNameEvent.php +++ b/src/DataDefinition/Palette/Builder/Event/SetLegendClassNameEvent.php @@ -22,6 +22,7 @@ namespace ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Builder\Event; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Builder\PaletteBuilder; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\LegendInterface; /** * This event gets emitted when a legend class name is set. @@ -33,15 +34,15 @@ class SetLegendClassNameEvent extends BuilderEvent /** * The class name. * - * @var string + * @var class-string */ protected $legendClassName; /** * Create a new instance. * - * @param string $legendClassName The class name. - * @param PaletteBuilder $paletteBuilder The palette builder in use. + * @param class-string $legendClassName The class name. + * @param PaletteBuilder $paletteBuilder The palette builder in use. */ public function __construct($legendClassName, PaletteBuilder $paletteBuilder) { @@ -52,13 +53,13 @@ public function __construct($legendClassName, PaletteBuilder $paletteBuilder) /** * Set the legend class name. * - * @param string $legendClassName The class name. + * @param class-string $legendClassName The class name. * * @return SetLegendClassNameEvent */ public function setLegendClassName($legendClassName) { - $this->legendClassName = (string) $legendClassName; + $this->legendClassName = $legendClassName; return $this; } @@ -66,7 +67,7 @@ public function setLegendClassName($legendClassName) /** * Retrieve the class name. * - * @return string + * @return class-string */ public function getLegendClassName() { diff --git a/src/DataDefinition/Palette/Builder/Event/SetPaletteClassNameEvent.php b/src/DataDefinition/Palette/Builder/Event/SetPaletteClassNameEvent.php index 01fd54dd..26caf6aa 100644 --- a/src/DataDefinition/Palette/Builder/Event/SetPaletteClassNameEvent.php +++ b/src/DataDefinition/Palette/Builder/Event/SetPaletteClassNameEvent.php @@ -22,6 +22,7 @@ namespace ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Builder\Event; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Builder\PaletteBuilder; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\PaletteInterface; /** * This event gets emitted when a palette class name is set. @@ -33,15 +34,15 @@ class SetPaletteClassNameEvent extends BuilderEvent /** * The palette class name. * - * @var string + * @var class-string */ protected $paletteClassName; /** * Create a new instance. * - * @param string $paletteClassName The class name. - * @param PaletteBuilder $paletteBuilder The palette builder in use. + * @param class-string $paletteClassName The class name. + * @param PaletteBuilder $paletteBuilder The palette builder in use. */ public function __construct($paletteClassName, PaletteBuilder $paletteBuilder) { @@ -52,13 +53,13 @@ public function __construct($paletteClassName, PaletteBuilder $paletteBuilder) /** * Set the class name. * - * @param string $paletteClassName The class name. + * @param class-string $paletteClassName The class name. * * @return SetPaletteClassNameEvent */ public function setPaletteClassName($paletteClassName) { - $this->paletteClassName = (string) $paletteClassName; + $this->paletteClassName = $paletteClassName; return $this; } @@ -66,7 +67,7 @@ public function setPaletteClassName($paletteClassName) /** * Retrieve the class name. * - * @return string + * @return class-string */ public function getPaletteClassName() { diff --git a/src/DataDefinition/Palette/Builder/Event/SetPaletteCollectionClassNameEvent.php b/src/DataDefinition/Palette/Builder/Event/SetPaletteCollectionClassNameEvent.php index 9fe476fe..ddc9e6cf 100644 --- a/src/DataDefinition/Palette/Builder/Event/SetPaletteCollectionClassNameEvent.php +++ b/src/DataDefinition/Palette/Builder/Event/SetPaletteCollectionClassNameEvent.php @@ -22,6 +22,7 @@ namespace ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Builder\Event; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Builder\PaletteBuilder; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\PaletteCollectionInterface; /** * This event gets emitted when a palette collection class name is set. @@ -33,15 +34,15 @@ class SetPaletteCollectionClassNameEvent extends BuilderEvent /** * The palette collection class name. * - * @var string + * @var class-string */ protected $className; /** * Create a new instance. * - * @param string $className The class name. - * @param PaletteBuilder $paletteBuilder The palette builder. + * @param class-string $className The class name. + * @param PaletteBuilder $paletteBuilder The palette builder. */ public function __construct($className, PaletteBuilder $paletteBuilder) { @@ -52,13 +53,13 @@ public function __construct($className, PaletteBuilder $paletteBuilder) /** * Set the class name. * - * @param string $className The class name. + * @param class-string $className The class name. * * @return SetPaletteCollectionClassNameEvent */ public function setPaletteCollectionClassName($className) { - $this->className = (string) $className; + $this->className = $className; return $this; } @@ -66,7 +67,7 @@ public function setPaletteCollectionClassName($className) /** * Retrieve the class name. * - * @return string + * @return class-string */ public function getPaletteCollectionClassName() { diff --git a/src/DataDefinition/Palette/Builder/Event/SetPaletteConditionChainClassNameEvent.php b/src/DataDefinition/Palette/Builder/Event/SetPaletteConditionChainClassNameEvent.php index f8afede7..7fa6c02c 100644 --- a/src/DataDefinition/Palette/Builder/Event/SetPaletteConditionChainClassNameEvent.php +++ b/src/DataDefinition/Palette/Builder/Event/SetPaletteConditionChainClassNameEvent.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,17 +14,22 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ namespace ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Builder\Event; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ConditionChainInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Builder\PaletteBuilder; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Condition\Palette\PaletteConditionInterface; /** * This event gets emitted when a palette condition chain class name is set. + * + * @psalm-type TConditionInterface=ConditionChainInterface&PaletteConditionInterface */ class SetPaletteConditionChainClassNameEvent extends BuilderEvent { @@ -33,15 +38,15 @@ class SetPaletteConditionChainClassNameEvent extends BuilderEvent /** * The class name. * - * @var string + * @var class-string */ protected $className; /** * Create a new instance. * - * @param string $className The class name. - * @param PaletteBuilder $paletteBuilder The palette builder in use. + * @param class-string $className The class name. + * @param PaletteBuilder $paletteBuilder The palette builder in use. */ public function __construct($className, PaletteBuilder $paletteBuilder) { @@ -52,20 +57,20 @@ public function __construct($className, PaletteBuilder $paletteBuilder) /** * Set the class name. * - * @param string $className The class name. + * @param class-string $className The class name. * * @return SetPaletteConditionChainClassNameEvent */ public function setPaletteConditionChainClassName($className) { - $this->className = (string) $className; + $this->className = $className; return $this; } /** * Retrieve the class name. * - * @return string + * @return class-string */ public function getPaletteConditionChainClassName() { diff --git a/src/DataDefinition/Palette/Builder/Event/SetPalettePropertyValueConditionClassNameEvent.php b/src/DataDefinition/Palette/Builder/Event/SetPalettePropertyValueConditionClassNameEvent.php index 46b92d1a..92b0c962 100644 --- a/src/DataDefinition/Palette/Builder/Event/SetPalettePropertyValueConditionClassNameEvent.php +++ b/src/DataDefinition/Palette/Builder/Event/SetPalettePropertyValueConditionClassNameEvent.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2020 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2020 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -22,6 +23,7 @@ namespace ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Builder\Event; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Builder\PaletteBuilder; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Condition\Palette\PaletteConditionInterface; /** * This event gets emitted when a palette property value condition class name is set. @@ -35,15 +37,15 @@ class SetPalettePropertyValueConditionClassNameEvent extends BuilderEvent /** * The class name. * - * @var string + * @var class-string */ protected $className; /** * Create a new instance. * - * @param string $className The class name. - * @param PaletteBuilder $paletteBuilder The palette builder in use. + * @param class-string $className The class name. + * @param PaletteBuilder $paletteBuilder The palette builder in use. */ public function __construct($className, PaletteBuilder $paletteBuilder) { @@ -54,13 +56,13 @@ public function __construct($className, PaletteBuilder $paletteBuilder) /** * Set the class name. * - * @param string $className The class name. + * @param class-string $className The class name. * * @return SetPalettePropertyValueConditionClassNameEvent */ public function setPalettePropertyValueConditionClassName($className) { - $this->className = (string) $className; + $this->className = $className; return $this; } @@ -68,7 +70,7 @@ public function setPalettePropertyValueConditionClassName($className) /** * Retrieve the class name. * - * @return string + * @return class-string */ public function getPalettePropertyValueConditionClassName() { diff --git a/src/DataDefinition/Palette/Builder/Event/SetPropertyClassNameEvent.php b/src/DataDefinition/Palette/Builder/Event/SetPropertyClassNameEvent.php index 1cdaf7bb..5c4e83a0 100644 --- a/src/DataDefinition/Palette/Builder/Event/SetPropertyClassNameEvent.php +++ b/src/DataDefinition/Palette/Builder/Event/SetPropertyClassNameEvent.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -22,6 +23,7 @@ namespace ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Builder\Event; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Builder\PaletteBuilder; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\PropertyInterface; /** * This event gets emitted when a property class name is set. @@ -33,15 +35,15 @@ class SetPropertyClassNameEvent extends BuilderEvent /** * The class name. * - * @var string + * @var class-string */ protected $propertyClassName; /** * Create a new instance. * - * @param string $propertyClassName The class name. - * @param PaletteBuilder $paletteBuilder The palette builder in use. + * @param class-string $propertyClassName The class name. + * @param PaletteBuilder $paletteBuilder The palette builder in use. */ public function __construct($propertyClassName, PaletteBuilder $paletteBuilder) { @@ -52,13 +54,13 @@ public function __construct($propertyClassName, PaletteBuilder $paletteBuilder) /** * Set the class name. * - * @param string $propertyClassName The class name. + * @param class-string $propertyClassName The class name. * * @return SetPropertyClassNameEvent */ public function setPropertyClassName($propertyClassName) { - $this->propertyClassName = (string) $propertyClassName; + $this->propertyClassName = $propertyClassName; return $this; } @@ -66,7 +68,7 @@ public function setPropertyClassName($propertyClassName) /** * Retrieve the class name. * - * @return string + * @return class-string */ public function getPropertyClassName() { diff --git a/src/DataDefinition/Palette/Builder/Event/SetPropertyConditionChainClassNameEvent.php b/src/DataDefinition/Palette/Builder/Event/SetPropertyConditionChainClassNameEvent.php index 35e41aad..237c086e 100644 --- a/src/DataDefinition/Palette/Builder/Event/SetPropertyConditionChainClassNameEvent.php +++ b/src/DataDefinition/Palette/Builder/Event/SetPropertyConditionChainClassNameEvent.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,17 +14,23 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ namespace ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Builder\Event; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ConditionChainInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Builder\PaletteBuilder; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Condition\Property\PropertyConditionInterface; /** * This event gets emitted when a property condition chain class name is set. + * + * @psalm-type TConditionInterface=ConditionChainInterface&PropertyConditionInterface + * */ class SetPropertyConditionChainClassNameEvent extends BuilderEvent { @@ -33,15 +39,15 @@ class SetPropertyConditionChainClassNameEvent extends BuilderEvent /** * The class name. * - * @var string + * @var class-string */ protected $className; /** * Create a new instance. * - * @param string $className The class name. - * @param PaletteBuilder $paletteBuilder The palette builder in use. + * @param class-string $className The class name. + * @param PaletteBuilder $paletteBuilder The palette builder in use. */ public function __construct($className, PaletteBuilder $paletteBuilder) { @@ -52,13 +58,13 @@ public function __construct($className, PaletteBuilder $paletteBuilder) /** * Set the class name. * - * @param string $className The class name. + * @param class-string $className The class name. * * @return SetPropertyConditionChainClassNameEvent */ public function setPalettePropertyConditionChainClassName($className) { - $this->className = (string) $className; + $this->className = $className; return $this; } @@ -66,7 +72,7 @@ public function setPalettePropertyConditionChainClassName($className) /** * Retrieve the class name. * - * @return string + * @return class-string */ public function getPalettePropertyConditionChainClassName() { diff --git a/src/DataDefinition/Palette/Builder/Event/SetPropertyValueConditionClassNameEvent.php b/src/DataDefinition/Palette/Builder/Event/SetPropertyValueConditionClassNameEvent.php index b5cdb359..59a49aba 100644 --- a/src/DataDefinition/Palette/Builder/Event/SetPropertyValueConditionClassNameEvent.php +++ b/src/DataDefinition/Palette/Builder/Event/SetPropertyValueConditionClassNameEvent.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -22,6 +23,7 @@ namespace ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Builder\Event; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Builder\PaletteBuilder; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Condition\Property\PropertyConditionInterface; /** * This event gets emitted when a property value condition class name is set. @@ -33,15 +35,15 @@ class SetPropertyValueConditionClassNameEvent extends BuilderEvent /** * The class name. * - * @var string + * @var class-string */ protected $className; /** * Create a new instance. * - * @param string $className The class name. - * @param PaletteBuilder $paletteBuilder The palette builder in use. + * @param class-string $className The class name. + * @param PaletteBuilder $paletteBuilder The palette builder in use. */ public function __construct($className, PaletteBuilder $paletteBuilder) { @@ -52,20 +54,20 @@ public function __construct($className, PaletteBuilder $paletteBuilder) /** * Set the class name. * - * @param string $className The class name. + * @param class-string $className The class name. * * @return SetPropertyValueConditionClassNameEvent */ public function setPropertyValueConditionClassName($className) { - $this->className = (string) $className; + $this->className = $className; return $this; } /** * Retrieve the class name. * - * @return string + * @return class-string */ public function getPropertyValueConditionClassName() { diff --git a/src/DataDefinition/Palette/Builder/PaletteBuilder.php b/src/DataDefinition/Palette/Builder/PaletteBuilder.php index e80c921f..df1fd138 100644 --- a/src/DataDefinition/Palette/Builder/PaletteBuilder.php +++ b/src/DataDefinition/Palette/Builder/PaletteBuilder.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2021 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -15,7 +15,8 @@ * @author Tristan Lins * @author Sven Baumann * @author Richard Henkenjohann - * @copyright 2013-2021 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -75,10 +76,20 @@ use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\PropertyInterface; use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralInvalidArgumentException; use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralRuntimeException; +use ReflectionClass; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; + +use function array_shift; +use function count; +use function func_get_args; +use function is_object; /** * The palette builder is used to build palette collections, palettes, legends, properties and conditions. * + * @psalm-type TPaletteConditionChainInterface=ConditionChainInterface&PaletteConditionInterface + * @psalm-type TPropertyConditionChainInterface=ConditionChainInterface&PropertyConditionInterface + * * @SuppressWarnings(PHPMD.LongVariable) * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) * @SuppressWarnings(PHPMD.TooManyPublicMethods) @@ -108,126 +119,126 @@ class PaletteBuilder /** * The class name of the class to use for palette collections. * - * @var string + * @var class-string */ protected $paletteCollectionClassName = PaletteCollection::class; /** * The class to use for palette collections. * - * @var \ReflectionClass + * @var ReflectionClass */ protected $paletteCollectionClass; /** * The class name of the class to use for palettes. * - * @var string + * @var class-string */ protected $paletteClassName = Palette::class; /** * The class to use for palettes. * - * @var \ReflectionClass + * @var ReflectionClass */ protected $paletteClass; /** * The class name of the class to use for palette legends. * - * @var string + * @var class-string */ protected $legendClassName = Legend::class; /** * The class to use for palette legends. * - * @var \ReflectionClass + * @var ReflectionClass */ protected $legendClass; /** * The class name of the class to use for palette properties. * - * @var string + * @var class-string */ protected $propertyClassName = Property::class; /** * The class to use for palette properties. * - * @var \ReflectionClass + * @var ReflectionClass */ protected $propertyClass; /** * The class name of the class to use for palette condition chains. * - * @var string + * @var class-string */ protected $paletteConditionChainClassName = PaletteConditionChain::class; /** - * The the class to use for palette condition chains. + * The class to use for palette condition chains. * - * @var \ReflectionClass + * @var ReflectionClass */ protected $paletteConditionChainClass; /** * The class name of the class to use for palette conditions. * - * @var string + * @var class-string */ protected $defaultPaletteConditionClassName = DefaultPaletteCondition::class; /** - * The the class to use for palette conditions. + * The class to use for palette conditions. * - * @var \ReflectionClass + * @var ReflectionClass */ protected $defaultPaletteConditionClass; /** * The class name of the class to use for property value conditions. * - * @var string + * @var class-string */ protected $palettePropertyValueConditionClassName = PropertyValueCondition::class; /** * The class to use for property value conditions. * - * @var \ReflectionClass + * @var ReflectionClass */ protected $palettePropertyValueConditionClass; /** * The class name of the class to use for property condition chains. * - * @var string + * @var class-string */ protected $propertyConditionChainClassName = PropertyConditionChain::class; /** * The class to use for property condition chains. * - * @var \ReflectionClass + * @var ReflectionClass */ protected $propertyConditionChainClass; /** * The class name of the class to use for property value conditions. * - * @var string + * @var class-string */ protected $propertyValueConditionClassName = PropertyPropertyValueCondition::class; /** - * The the class to use for property value conditions. + * The class to use for property value conditions. * - * @var \ReflectionClass + * @var ReflectionClass */ protected $propertyValueConditionClass; @@ -255,14 +266,14 @@ class PaletteBuilder /** * The property currently working on. * - * @var PropertyInterface|null + * @var list|PropertyInterface|null */ protected $property; /** * The condition currently working on. * - * @var PropertyConditionInterface|PaletteConditionInterface|ConditionChainInterface|null + * @var PropertyConditionInterface|PaletteConditionInterface|null */ protected $condition; @@ -271,7 +282,7 @@ class PaletteBuilder * * @param ContainerInterface $container The data definition container for which the palettes shall get built. * - * @return PaletteBuilder + * @return self */ public static function create(ContainerInterface $container) { @@ -287,17 +298,17 @@ public function __construct(ContainerInterface $container) { $this->container = $container; - $this->paletteCollectionClass = new \ReflectionClass($this->paletteCollectionClassName); - $this->paletteClass = new \ReflectionClass($this->paletteClassName); - $this->legendClass = new \ReflectionClass($this->legendClassName); - $this->propertyClass = new \ReflectionClass($this->propertyClassName); + $this->paletteCollectionClass = new ReflectionClass($this->paletteCollectionClassName); + $this->paletteClass = new ReflectionClass($this->paletteClassName); + $this->legendClass = new ReflectionClass($this->legendClassName); + $this->propertyClass = new ReflectionClass($this->propertyClassName); - $this->paletteConditionChainClass = new \ReflectionClass($this->paletteConditionChainClassName); - $this->defaultPaletteConditionClass = new \ReflectionClass($this->defaultPaletteConditionClassName); - $this->palettePropertyValueConditionClass = new \ReflectionClass($this->palettePropertyValueConditionClassName); + $this->paletteConditionChainClass = new ReflectionClass($this->paletteConditionChainClassName); + $this->defaultPaletteConditionClass = new ReflectionClass($this->defaultPaletteConditionClassName); + $this->palettePropertyValueConditionClass = new ReflectionClass($this->palettePropertyValueConditionClassName); - $this->propertyConditionChainClass = new \ReflectionClass($this->propertyConditionChainClassName); - $this->propertyValueConditionClass = new \ReflectionClass($this->propertyValueConditionClassName); + $this->propertyConditionChainClass = new ReflectionClass($this->propertyConditionChainClassName); + $this->propertyValueConditionClass = new ReflectionClass($this->propertyValueConditionClassName); } /** @@ -313,9 +324,9 @@ public function getContainer() /** * Set the palette collection class name. * - * @param string $paletteCollectionClassName The class name. + * @param class-string $paletteCollectionClassName The class name. * - * @return PaletteBuilder + * @return self */ public function setPaletteCollectionClassName($paletteCollectionClassName) { @@ -323,15 +334,16 @@ public function setPaletteCollectionClassName($paletteCollectionClassName) $this->dispatchEvent($event); $paletteCollectionClassName = $event->getPaletteCollectionClassName(); - $this->paletteCollectionClassName = (string) $paletteCollectionClassName; - $this->paletteCollectionClass = new \ReflectionClass($this->paletteCollectionClassName); + $this->paletteCollectionClassName = $paletteCollectionClassName; + $this->paletteCollectionClass = new ReflectionClass($this->paletteCollectionClassName); + return $this; } /** * Return the palette collection class name. * - * @return string + * @return class-string */ public function getPaletteCollectionClassName() { @@ -341,9 +353,9 @@ public function getPaletteCollectionClassName() /** * Set the palette class name. * - * @param string $paletteClassName The class name. + * @param class-string $paletteClassName The class name. * - * @return PaletteBuilder + * @return self */ public function setPaletteClassName($paletteClassName) { @@ -351,15 +363,16 @@ public function setPaletteClassName($paletteClassName) $this->dispatchEvent($event); $paletteClassName = $event->getPaletteClassName(); - $this->paletteClassName = (string) $paletteClassName; - $this->paletteClass = new \ReflectionClass($this->paletteClassName); + $this->paletteClassName = $paletteClassName; + $this->paletteClass = new ReflectionClass($this->paletteClassName); + return $this; } /** * Return the palette class name. * - * @return string + * @return class-string */ public function getPaletteClassName() { @@ -369,9 +382,9 @@ public function getPaletteClassName() /** * Set the legend class name. * - * @param string $legendClassName The class name. + * @param class-string $legendClassName The class name. * - * @return PaletteBuilder + * @return self */ public function setLegendClassName($legendClassName) { @@ -379,15 +392,16 @@ public function setLegendClassName($legendClassName) $this->dispatchEvent($event); $legendClassName = $event->getLegendClassName(); - $this->legendClassName = (string) $legendClassName; - $this->legendClass = new \ReflectionClass($this->legendClassName); + $this->legendClassName = $legendClassName; + $this->legendClass = new ReflectionClass($this->legendClassName); + return $this; } /** * Return the legend class name. * - * @return string + * @return class-string */ public function getLegendClassName() { @@ -397,9 +411,9 @@ public function getLegendClassName() /** * Set the property class name. * - * @param string $propertyClassName The class name. + * @param class-string $propertyClassName The class name. * - * @return PaletteBuilder + * @return self */ public function setPropertyClassName($propertyClassName) { @@ -407,15 +421,16 @@ public function setPropertyClassName($propertyClassName) $this->dispatchEvent($event); $propertyClassName = $event->getPropertyClassName(); - $this->propertyClassName = (string) $propertyClassName; - $this->propertyClass = new \ReflectionClass($this->propertyClassName); + $this->propertyClassName = $propertyClassName; + $this->propertyClass = new ReflectionClass($this->propertyClassName); + return $this; } /** * Return the property class name. * - * @return string + * @return class-string */ public function getPropertyClassName() { @@ -425,9 +440,9 @@ public function getPropertyClassName() /** * Set the palette condition chain class name. * - * @param string $paletteConditionChainClassName The class name. + * @param class-string $paletteConditionChainClassName The class name. * - * @return PaletteBuilder + * @return self */ public function setPaletteConditionChainClassName($paletteConditionChainClassName) { @@ -435,15 +450,16 @@ public function setPaletteConditionChainClassName($paletteConditionChainClassNam $this->dispatchEvent($event); $paletteConditionChainClassName = $event->getPaletteConditionChainClassName(); - $this->paletteConditionChainClassName = (string) $paletteConditionChainClassName; - $this->paletteConditionChainClass = new \ReflectionClass($this->paletteConditionChainClassName); + $this->paletteConditionChainClassName = $paletteConditionChainClassName; + $this->paletteConditionChainClass = new ReflectionClass($this->paletteConditionChainClassName); + return $this; } /** * Return the palette condition chain class name. * - * @return string + * @return class-string */ public function getPaletteConditionChainClassName() { @@ -463,9 +479,9 @@ public function getPaletteCollection() /** * Set the default palette condition class name. * - * @param string $defaultPaletteConditionClassName The class name. + * @param class-string $defaultPaletteConditionClassName The class name. * - * @return PaletteBuilder + * @return self */ public function setDefaultPaletteConditionClassName($defaultPaletteConditionClassName) { @@ -473,15 +489,16 @@ public function setDefaultPaletteConditionClassName($defaultPaletteConditionClas $this->dispatchEvent($event); $defaultPaletteConditionClassName = $event->getDefaultPaletteConditionClassName(); - $this->defaultPaletteConditionClassName = (string) $defaultPaletteConditionClassName; - $this->defaultPaletteConditionClass = new \ReflectionClass($this->defaultPaletteConditionClassName); + $this->defaultPaletteConditionClassName = $defaultPaletteConditionClassName; + $this->defaultPaletteConditionClass = new ReflectionClass($this->defaultPaletteConditionClassName); + return $this; } /** * Return the default palette condition class name. * - * @return string + * @return class-string */ public function getDefaultPaletteConditionClassName() { @@ -491,9 +508,9 @@ public function getDefaultPaletteConditionClassName() /** * Set the palette property value condition class name. * - * @param string $palettePropertyValueConditionClassName The class name. + * @param class-string $palettePropertyValueConditionClassName The class name. * - * @return PaletteBuilder + * @return self */ public function setPalettePropertyValueConditionClassName($palettePropertyValueConditionClassName) { @@ -501,17 +518,18 @@ public function setPalettePropertyValueConditionClassName($palettePropertyValueC $this->dispatchEvent($event); $palettePropertyValueConditionClassName = $event->getPalettePropertyValueConditionClassName(); - $this->palettePropertyValueConditionClassName = (string) $palettePropertyValueConditionClassName; - $this->palettePropertyValueConditionClass = new \ReflectionClass( + $this->palettePropertyValueConditionClassName = $palettePropertyValueConditionClassName; + $this->palettePropertyValueConditionClass = new ReflectionClass( $this->palettePropertyValueConditionClassName ); + return $this; } /** * Return the palette property value condition class name. * - * @return string + * @return class-string */ public function getPalettePropertyValueConditionClassName() { @@ -521,9 +539,9 @@ public function getPalettePropertyValueConditionClassName() /** * Set the property condition chain class name. * - * @param string $propertyConditionChainClassName The class name. + * @param class-string $propertyConditionChainClassName The class name. * - * @return PaletteBuilder + * @return self */ public function setPropertyConditionChainClassName($propertyConditionChainClassName) { @@ -531,15 +549,16 @@ public function setPropertyConditionChainClassName($propertyConditionChainClassN $this->dispatchEvent($event); $propertyConditionChainClassName = $event->getPalettePropertyConditionChainClassName(); - $this->propertyConditionChainClassName = (string) $propertyConditionChainClassName; - $this->propertyConditionChainClass = new \ReflectionClass($this->propertyConditionChainClassName); + $this->propertyConditionChainClassName = $propertyConditionChainClassName; + $this->propertyConditionChainClass = new ReflectionClass($this->propertyConditionChainClassName); + return $this; } /** * Return the property condition chain class name. * - * @return string + * @return class-string */ public function getPropertyConditionChainClassName() { @@ -549,9 +568,9 @@ public function getPropertyConditionChainClassName() /** * Set the property value condition class name. * - * @param string $propertyValueConditionClassName The class name. + * @param class-string $propertyValueConditionClassName The class name. * - * @return PaletteBuilder + * @return self */ public function setPropertyValueConditionClassName($propertyValueConditionClassName) { @@ -559,15 +578,16 @@ public function setPropertyValueConditionClassName($propertyValueConditionClassN $this->dispatchEvent($event); $propertyValueConditionClassName = $event->getPropertyValueConditionClassName(); - $this->propertyValueConditionClassName = (string) $propertyValueConditionClassName; - $this->propertyValueConditionClass = new \ReflectionClass($this->propertyValueConditionClassName); + $this->propertyValueConditionClassName = $propertyValueConditionClassName; + $this->propertyValueConditionClass = new ReflectionClass($this->propertyValueConditionClassName); + return $this; } /** * Return the property value condition class name. * - * @return string + * @return class-string */ public function getPropertyValueConditionClassName() { @@ -597,7 +617,7 @@ public function getLegend() /** * Return the current property object. * - * @return PropertyInterface|null + * @return list|PropertyInterface|null */ public function getProperty() { @@ -607,7 +627,7 @@ public function getProperty() /** * Return the current condition object. * - * @return PaletteConditionInterface|PropertyConditionInterface|null + * @return PropertyConditionInterface|PaletteConditionInterface|null */ public function getCondition() { @@ -619,7 +639,7 @@ public function getCondition() * * @param PaletteCollectionInterface $paletteCollection The palette collection to reuse. * - * @return PaletteBuilder + * @return self */ public function usePaletteCollection(PaletteCollectionInterface $paletteCollection) { @@ -637,7 +657,7 @@ public function usePaletteCollection(PaletteCollectionInterface $paletteCollecti /** * Start a new palette collection. * - * @return PaletteBuilder + * @return self */ public function createPaletteCollection() { @@ -659,7 +679,7 @@ public function createPaletteCollection() * * @param PaletteCollectionInterface $collection Return the final palette collection. * - * @return PaletteBuilder + * @return self * * @throws DcGeneralRuntimeException When no collection is stored. */ @@ -689,7 +709,7 @@ public function finishPaletteCollection(&$collection = null) * * @param PaletteInterface $palette The palette to reuse. * - * @return PaletteBuilder + * @return self */ public function usePalette(PaletteInterface $palette) { @@ -710,7 +730,7 @@ public function usePalette(PaletteInterface $palette) * @param string|null $name Only for backwards compatibility, We will remove palette names in the future * (deprecated). * - * @return PaletteBuilder + * @return self */ public function createPalette($name = null) { @@ -736,7 +756,7 @@ public function createPalette($name = null) * * @param PaletteInterface $palette Return the final palette. * - * @return PaletteBuilder + * @return self * * @throws DcGeneralRuntimeException When no palette is stored in the builder. */ @@ -771,7 +791,7 @@ public function finishPalette(&$palette = null) * * @param LegendInterface $legend The legend. * - * @return PaletteBuilder + * @return self */ public function useLegend(LegendInterface $legend) { @@ -791,7 +811,7 @@ public function useLegend(LegendInterface $legend) * * @param string $name Name of the legend. * - * @return PaletteBuilder + * @return self */ public function createLegend($name) { @@ -813,7 +833,7 @@ public function createLegend($name) * * @param LegendInterface $legend Return the final legend. * - * @return PaletteBuilder + * @return self * * @throws DcGeneralRuntimeException When no legend is stored in the builder. */ @@ -846,7 +866,7 @@ public function finishLegend(&$legend = null) * @param PropertyInterface $property The first property. * @param PropertyInterface $_ Any more subsequent properties to be used. * - * @return PaletteBuilder + * @return self * * @SuppressWarnings(PHPMD.UnusedFormalParameter) * @SuppressWarnings(PHPMD.ShortVariable) @@ -858,7 +878,8 @@ public function useProperty($property, $_ = null) $this->finishProperty(); } - $properties = \func_get_args(); + /** @var list $properties */ + $properties = func_get_args(); $this->property = []; foreach ($properties as $property) { @@ -868,8 +889,8 @@ public function useProperty($property, $_ = null) $this->property[] = $property; } - if (1 === \count($this->property)) { - $this->property = \array_shift($this->property); + if (1 === count($this->property)) { + $this->property = array_shift($this->property); } return $this; @@ -881,7 +902,7 @@ public function useProperty($property, $_ = null) * @param string $propertyName The name of the property. * @param PropertyInterface $_ Any more subsequent property names to be used. * - * @return PaletteBuilder + * @return self * * @SuppressWarnings(PHPMD.UnusedFormalParameter) * @SuppressWarnings(PHPMD.ShortVariable) @@ -893,7 +914,7 @@ public function createProperty($propertyName, $_ = null) $this->finishProperty(); } - $propertyNames = \func_get_args(); + $propertyNames = func_get_args(); $this->property = []; foreach ($propertyNames as $propertyName) { @@ -906,8 +927,8 @@ public function createProperty($propertyName, $_ = null) $this->property[] = $property; } - if (1 === \count($this->property)) { - $this->property = \array_shift($this->property); + if (1 === count($this->property)) { + $this->property = array_shift($this->property); } return $this; @@ -918,7 +939,7 @@ public function createProperty($propertyName, $_ = null) * * @param PropertyInterface|PropertyInterface[] $property Return the final property or set of properties. * - * @return PaletteBuilder + * @return self * * @throws DcGeneralRuntimeException When no property is stored in the builder. */ @@ -932,7 +953,7 @@ public function finishProperty(&$property = null) $this->finishCondition(); } - $properties = \is_object($this->property) ? [$this->property] : $this->property; + $properties = is_object($this->property) ? [$this->property] : $this->property; foreach ($properties as $index => $tempProperty) { $event = new FinishPropertyEvent($tempProperty, $this); @@ -954,11 +975,13 @@ public function finishProperty(&$property = null) /** * Create a palette condition chain. * - * @return PaletteBuilder + * @return self + * + * @psalm-assert ConditionChainInterface $this->condition */ protected function createPaletteConditionChain() { - if (!$this->condition instanceof PaletteConditionChain) { + if (!$this->condition instanceof ConditionChainInterface) { $previousCondition = $this->condition; $condition = $this->paletteConditionChainClass->newInstance(); @@ -969,8 +992,12 @@ protected function createPaletteConditionChain() $event = new CreateConditionEvent($condition, $this); $this->dispatchEvent($event); $condition = $event->getCondition(); - /** @var ConditionChainInterface $condition */ - $condition->addCondition($previousCondition); + assert($condition instanceof PaletteConditionInterface); + assert($condition instanceof ConditionChainInterface); + + if ($previousCondition) { + $condition->addCondition($previousCondition); + } $this->condition = $condition; } @@ -983,12 +1010,15 @@ protected function createPaletteConditionChain() * * @param string $conjunction The conjunction to use (defaults to AND). * - * @return PaletteBuilder + * @return self + * + * @psalm-assert ConditionChainInterface $this->condition */ - protected function createPropertyConditionChain($conjunction = PropertyConditionChain::AND_CONJUNCTION) + protected function createPropertyConditionChain($conjunction = ConditionChainInterface::AND_CONJUNCTION) { if ( - !($this->condition instanceof PropertyConditionChain) + // FIXME: We need a condition chain interface here to allow proper overriding. + !($this->condition instanceof ConditionChainInterface) || ($conjunction !== $this->condition->getConjunction()) ) { $previousCondition = $this->condition; @@ -1001,8 +1031,9 @@ protected function createPropertyConditionChain($conjunction = PropertyCondition $event = new CreateConditionEvent($condition, $this); $this->dispatchEvent($event); $condition = $event->getCondition(); - /** @var ConditionChainInterface $condition */ - $condition->addCondition($previousCondition); + if (null !== $previousCondition) { + $condition->addCondition($previousCondition); + } $this->condition = $condition; } @@ -1013,7 +1044,7 @@ protected function createPropertyConditionChain($conjunction = PropertyCondition /** * Start a new default-palette condition. * - * @return PaletteBuilder + * @return self * * @throws DcGeneralRuntimeException When no palette or property has been stored. */ @@ -1044,7 +1075,7 @@ public function createDefaultPaletteCondition() /** * Start a new default-palette condition and chain with previous condition. * - * @return PaletteBuilder + * @return self * * @throws DcGeneralRuntimeException When no palette or property has been stored. */ @@ -1079,7 +1110,7 @@ public function chainDefaultPaletteCondition() * @param mixed $propertyValue The value of the property. * @param bool $strict Flag if the comparison shall be strict (type safe). * - * @return PaletteBuilder + * @return self * * @throws DcGeneralRuntimeException If neither a palette nor a property is stored in the builder. */ @@ -1098,15 +1129,17 @@ public function createPropertyValueCondition($propertyName, $propertyValue, $str 'Does not know where to create the property-value condition, please create a palette or property first' ); } - + // FIXME: we need an interface for PropertyValueCondition + assert( + ($condition instanceof PropertyValueCondition) + || ($condition instanceof PropertyPropertyValueCondition) + ); $condition->setPropertyName($propertyName); $condition->setPropertyValue($propertyValue); $condition->setStrict($strict); - $event = new CreatePropertyValueConditionEvent($condition, $this); $this->dispatchEvent($event); $condition = $event->getPropertyValueCondition(); - $event = new CreateConditionEvent($condition, $this); $this->dispatchEvent($event); $condition = $event->getCondition(); @@ -1124,7 +1157,7 @@ public function createPropertyValueCondition($propertyName, $propertyValue, $str * @param bool $strict Flag if the comparison shall be strict (type safe). * @param string $conjunction The conjunction. * - * @return PaletteBuilder + * @return self * * @throws DcGeneralRuntimeException If neither a palette nor a property is stored in the builder. */ @@ -1146,14 +1179,17 @@ public function chainPropertyValueCondition( ); } + // FIXME: we need an interface for PropertyValueCondition + assert( + ($condition instanceof PropertyValueCondition) + || ($condition instanceof PropertyPropertyValueCondition) + ); $condition->setPropertyName($propertyName); $condition->setPropertyValue($propertyValue); $condition->setStrict($strict); - $event = new CreatePropertyValueConditionEvent($condition, $this); $this->dispatchEvent($event); $condition = $event->getPropertyValueCondition(); - $event = new CreateConditionEvent($condition, $this); $this->dispatchEvent($event); $condition = $event->getCondition(); @@ -1168,7 +1204,7 @@ public function chainPropertyValueCondition( * * @param PropertyConditionInterface|PaletteConditionInterface $condition Return the final condition. * - * @return PaletteBuilder + * @return self * * @throws DcGeneralRuntimeException If no condition is stored in the builder. */ @@ -1230,17 +1266,19 @@ protected function addPaletteCondition(PaletteConditionInterface $condition) * @return void * * @throws DcGeneralRuntimeException If the the property is missing. + * + * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ protected function addPropertyCondition(PropertyConditionInterface $condition, $scope = self::VISIBLE) { - if (!$this->property) { + if (null === $this->property || [] === $this->property) { throw new DcGeneralRuntimeException('Property is missing, please create a property first'); } - $properties = \is_object($this->property) ? [$this->property] : $this->property; + $properties = ($this->property instanceof PropertyInterface) ? [$this->property] : $this->property; foreach ($properties as $property) { - /** @var PropertyInterface $property */ + /** @var AddConditionEvent $event */ $event = new AddConditionEvent($condition, $property, $this); $this->dispatchEvent($event); $condition = $event->getCondition(); @@ -1277,7 +1315,7 @@ protected function addPropertyCondition(PropertyConditionInterface $condition, $ * @param PaletteConditionInterface|PropertyConditionInterface $condition The condition to add. * @param string $scope The scope. * - * @return PaletteBuilder + * @return self * * @throws DcGeneralInvalidArgumentException When an unknown condition type is passed. */ @@ -1293,8 +1331,9 @@ public function addCondition($condition, $scope = self::VISIBLE) return $this; } - $type = \is_object($condition) ? \get_class($condition) : \gettype($condition); - throw new DcGeneralInvalidArgumentException('Cannot handle condition of type [' . $type . ']'); + throw new DcGeneralInvalidArgumentException( + 'Cannot handle condition of type [' . \get_debug_type($condition) . ']' + ); } /** @@ -1309,6 +1348,8 @@ public function addCondition($condition, $scope = self::VISIBLE) protected function dispatchEvent(BuilderEvent $event): void { $dispatcher = System::getContainer()->get('event_dispatcher'); + assert($dispatcher instanceof EventDispatcherInterface); + $dispatcher->dispatch($event, $event::NAME); } } diff --git a/src/DataDefinition/Palette/Condition/Palette/AbstractBoolPaletteCondition.php b/src/DataDefinition/Palette/Condition/Palette/AbstractBoolPaletteCondition.php index 989f93cd..9726282b 100644 --- a/src/DataDefinition/Palette/Condition/Palette/AbstractBoolPaletteCondition.php +++ b/src/DataDefinition/Palette/Condition/Palette/AbstractBoolPaletteCondition.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -49,8 +50,8 @@ abstract class AbstractBoolPaletteCondition extends AbstractWeightAwarePaletteCo */ public function __construct($propertyName = '', $strict = false, $weight = 1) { - $this->propertyName = (string) $propertyName; - $this->strict = (bool) $strict; + $this->propertyName = $propertyName; + $this->strict = $strict; $this->setWeight($weight); } @@ -63,7 +64,7 @@ public function __construct($propertyName = '', $strict = false, $weight = 1) */ public function setPropertyName($propertyName) { - $this->propertyName = (string) $propertyName; + $this->propertyName = $propertyName; return $this; } @@ -87,7 +88,7 @@ public function getPropertyName() */ public function setStrict($strict) { - $this->strict = (bool) $strict; + $this->strict = $strict; return $this; } diff --git a/src/DataDefinition/Palette/Condition/Palette/PaletteConditionChain.php b/src/DataDefinition/Palette/Condition/Palette/PaletteConditionChain.php index d453088b..51e4866a 100644 --- a/src/DataDefinition/Palette/Condition/Palette/PaletteConditionChain.php +++ b/src/DataDefinition/Palette/Condition/Palette/PaletteConditionChain.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -34,7 +35,7 @@ class PaletteConditionChain extends AbstractConditionChain implements PaletteCon /** * {@inheritdoc} * - * @throws DcGeneralRuntimeException When an condition that does not implement PaletteConditionInterface + * @throws DcGeneralRuntimeException When a condition that does not implement PaletteConditionInterface * is encountered. */ public function getMatchCount(ModelInterface $model = null, PropertyValueBag $input = null) @@ -49,6 +50,9 @@ public function getMatchCount(ModelInterface $model = null, PropertyValueBag $in $conditionCount = $condition->getMatchCount($model, $input); if (false !== $conditionCount) { + if (false === $totalCount) { + $totalCount = 0; + } $totalCount += $conditionCount; } elseif (static::AND_CONJUNCTION === $this->conjunction) { return false; diff --git a/src/DataDefinition/Palette/Condition/Palette/PaletteConditionInterface.php b/src/DataDefinition/Palette/Condition/Palette/PaletteConditionInterface.php index 2af7520f..1e57e5e3 100644 --- a/src/DataDefinition/Palette/Condition/Palette/PaletteConditionInterface.php +++ b/src/DataDefinition/Palette/Condition/Palette/PaletteConditionInterface.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -40,10 +41,10 @@ interface PaletteConditionInterface extends ConditionInterface * When the condition does match, it must return a numeric value, the value may be negative or positive and even * zero. * - * @param ModelInterface|null $model If given, selectors will be evaluated depending on the model. - * @param PropertyValueBag $input If given, selectors will be evaluated depending on the input data. + * @param ModelInterface|null $model If given, selectors will be evaluated depending on the model. + * @param PropertyValueBag|null $input If given, selectors will be evaluated depending on the input data. * - * @return bool|int + * @return false|int */ public function getMatchCount(ModelInterface $model = null, PropertyValueBag $input = null); diff --git a/src/DataDefinition/Palette/Condition/Palette/PropertyValueCondition.php b/src/DataDefinition/Palette/Condition/Palette/PropertyValueCondition.php index c29f4320..c6c2843a 100644 --- a/src/DataDefinition/Palette/Condition/Palette/PropertyValueCondition.php +++ b/src/DataDefinition/Palette/Condition/Palette/PropertyValueCondition.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -60,9 +61,9 @@ class PropertyValueCondition extends AbstractWeightAwarePaletteCondition */ public function __construct($propertyName = '', $propertyValue = null, $strict = false, $weight = 1) { - $this->propertyName = (string) $propertyName; + $this->propertyName = $propertyName; $this->propertyValue = $propertyValue; - $this->strict = (bool) $strict; + $this->strict = $strict; $this->setWeight($weight); } @@ -75,7 +76,7 @@ public function __construct($propertyName = '', $propertyValue = null, $strict = */ public function setPropertyName($propertyName) { - $this->propertyName = (string) $propertyName; + $this->propertyName = $propertyName; return $this; } @@ -121,7 +122,7 @@ public function getPropertyValue() */ public function setStrict($strict) { - $this->strict = (bool) $strict; + $this->strict = $strict; return $this; } diff --git a/src/DataDefinition/Palette/Condition/Property/BooleanCondition.php b/src/DataDefinition/Palette/Condition/Property/BooleanCondition.php index b322c32b..889dd176 100644 --- a/src/DataDefinition/Palette/Condition/Property/BooleanCondition.php +++ b/src/DataDefinition/Palette/Condition/Property/BooleanCondition.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -45,7 +46,7 @@ class BooleanCondition implements PropertyConditionInterface */ public function __construct($value) { - $this->value = (bool) $value; + $this->value = $value; } /** @@ -57,7 +58,7 @@ public function __construct($value) */ public function setValue($value) { - $this->value = (bool) $value; + $this->value = $value; return $this; } diff --git a/src/DataDefinition/Palette/Condition/Property/DumpingPropertyCondition.php b/src/DataDefinition/Palette/Condition/Property/DumpingPropertyCondition.php index 14798ea7..8a04ce57 100644 --- a/src/DataDefinition/Palette/Condition/Property/DumpingPropertyCondition.php +++ b/src/DataDefinition/Palette/Condition/Property/DumpingPropertyCondition.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2022 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -15,7 +15,8 @@ * @author Tristan Lins * @author Sven Baumann * @author David Molineus - * @copyright 2013-2022 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -54,6 +55,8 @@ public function __construct($propertyCondition) * {@inheritdoc} * * @SuppressWarnings(PHPMD.DevelopmentCodeFragment) + * + * @psalm-suppress ForbiddenCode - We explicitly allow var_dump() here for debugging purposes. */ public function match( ModelInterface $model = null, @@ -63,7 +66,7 @@ public function match( ) { $result = $this->propertyCondition->match($model, $input, $property, $legend); - // @codingStandardsIgnoreStart - We explicitely allow var_dump() here for debugging purposes. + // @codingStandardsIgnoreStart - We explicitly allow var_dump() here for debugging purposes. echo '
$condition: 
'; \var_dump($this->propertyCondition); echo '
$model: 
'; diff --git a/src/DataDefinition/Palette/Condition/Property/PropertyBooleanConditionTrait.php b/src/DataDefinition/Palette/Condition/Property/PropertyBooleanConditionTrait.php new file mode 100644 index 00000000..f9830418 --- /dev/null +++ b/src/DataDefinition/Palette/Condition/Property/PropertyBooleanConditionTrait.php @@ -0,0 +1,111 @@ + + * @copyright 2013-2023 Contao Community Alliance. + * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later + * @filesource + */ + +namespace ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Condition\Property; + +/** + * Condition checking that the value of a property is false. + * + * @require-implements PropertyConditionInterface + */ +trait PropertyBooleanConditionTrait +{ + /** + * The property name. + * + * @var string + */ + protected $propertyName; + + /** + * Use strict compare mode. + * + * @var bool + */ + protected $strict; + + /** + * Create a new instance. + * + * @param string $propertyName The name of the property. + * @param bool $strict Flag if the comparison shall be strict (type safe). + */ + public function __construct($propertyName, $strict = false) + { + $this->propertyName = $propertyName; + $this->strict = $strict; + } + + /** + * Set the property name. + * + * @param string $propertyName The property name. + * + * @return self + */ + public function setPropertyName($propertyName) + { + $this->propertyName = $propertyName; + + return $this; + } + + /** + * Retrieve the property name. + * + * @return string + */ + public function getPropertyName() + { + return $this->propertyName; + } + + /** + * Set the flag if the comparison shall be strict (type safe). + * + * @param boolean $strict The flag. + * + * @return self + */ + public function setStrict($strict) + { + $this->strict = $strict; + + return $this; + } + + /** + * Retrieve the flag if the comparison shall be strict (type safe). + * + * @return boolean + * + * @SuppressWarnings(PHPMD.BooleanGetMethodName) + */ + public function getStrict() + { + return $this->strict; + } + + /** + * {@inheritdoc} + */ + public function __clone() + { + } +} diff --git a/src/DataDefinition/Palette/Condition/Property/PropertyConditionInterface.php b/src/DataDefinition/Palette/Condition/Property/PropertyConditionInterface.php index 204e712f..e9ff3aad 100644 --- a/src/DataDefinition/Palette/Condition/Property/PropertyConditionInterface.php +++ b/src/DataDefinition/Palette/Condition/Property/PropertyConditionInterface.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -35,14 +36,14 @@ interface PropertyConditionInterface extends ConditionInterface /** * Check if the condition match. * - * @param ModelInterface|null $model If given, subpalettes will be evaluated depending on the model. - * If no model is given, all properties will be returned, including subpalette - * properties. - * @param PropertyValueBag $input If given, subpalettes will be evaluated depending on the input data. - * If no model and no input data is given, all properties will be returned, - * including subpalette properties. - * @param PropertyInterface $property The defined property. - * @param LegendInterface $legend The legend the property is assigned to. + * @param ModelInterface|null $model If given, subpalettes will be evaluated depending on the model. + * If no model is given, all properties will be returned, including + * subpalette properties. + * @param PropertyValueBag|null $input If given, subpalettes will be evaluated depending on the input data. + * If no model and no input data is given, all properties will be returned, + * including subpalette properties. + * @param PropertyInterface|null $property The defined property. + * @param LegendInterface|null $legend The legend the property is assigned to. * * @return bool */ diff --git a/src/DataDefinition/Palette/Condition/Property/PropertyEditableCondition.php b/src/DataDefinition/Palette/Condition/Property/PropertyEditableCondition.php index e8b030c3..ecda816c 100644 --- a/src/DataDefinition/Palette/Condition/Property/PropertyEditableCondition.php +++ b/src/DataDefinition/Palette/Condition/Property/PropertyEditableCondition.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -45,7 +46,7 @@ class PropertyEditableCondition implements PropertyConditionInterface */ public function __construct($propertyName = '') { - $this->propertyName = (string) $propertyName; + $this->propertyName = $propertyName; } /** @@ -57,7 +58,7 @@ public function __construct($propertyName = '') */ public function setPropertyName($propertyName) { - $this->propertyName = (string) $propertyName; + $this->propertyName = $propertyName; return $this; } @@ -84,8 +85,8 @@ public function match( return false; } - if ($legend->getPalette()) { - return $legend->getPalette()->getProperty($this->propertyName)->isEditable($model, $input, $legend); + if (null !== ($palette = $legend->getPalette())) { + return $palette->getProperty($this->propertyName)->isEditable($model, $input, $legend); } return $legend->getProperty($this->propertyName)->isEditable($model, $input, $legend); diff --git a/src/DataDefinition/Palette/Condition/Property/PropertyFalseCondition.php b/src/DataDefinition/Palette/Condition/Property/PropertyFalseCondition.php index 7dfb9361..9bee678b 100644 --- a/src/DataDefinition/Palette/Condition/Property/PropertyFalseCondition.php +++ b/src/DataDefinition/Palette/Condition/Property/PropertyFalseCondition.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -31,81 +32,7 @@ */ class PropertyFalseCondition implements PropertyConditionInterface { - /** - * The property name. - * - * @var string - */ - protected $propertyName; - - /** - * Use strict compare mode. - * - * @var bool - */ - protected $strict; - - /** - * Create a new instance. - * - * @param string $propertyName The name of the property. - * @param bool $strict Flag if the comparison shall be strict (type safe). - */ - public function __construct($propertyName, $strict = false) - { - $this->propertyName = (string) $propertyName; - $this->strict = (bool) $strict; - } - - /** - * Set the property name. - * - * @param string $propertyName The property name. - * - * @return PropertyFalseCondition - */ - public function setPropertyName($propertyName) - { - $this->propertyName = (string) $propertyName; - - return $this; - } - - /** - * Retrieve the property name. - * - * @return string - */ - public function getPropertyName() - { - return $this->propertyName; - } - - /** - * Set the flag if the comparison shall be strict (type safe). - * - * @param boolean $strict The flag. - * - * @return PropertyFalseCondition - */ - public function setStrict($strict) - { - $this->strict = (bool) $strict; - - return $this; - } - - /** - * Retrieve the flag if the comparison shall be strict (type safe). - * - * @return boolean - * - * @SuppressWarnings(PHPMD.BooleanGetMethodName) - */ - public function getStrict() - { - return $this->strict; - } + use PropertyBooleanConditionTrait; /** * {@inheritdoc} @@ -128,11 +55,4 @@ public function match( return $this->strict ? ($value === false) : !$value; } - - /** - * {@inheritdoc} - */ - public function __clone() - { - } } diff --git a/src/DataDefinition/Palette/Condition/Property/PropertyTrueCondition.php b/src/DataDefinition/Palette/Condition/Property/PropertyTrueCondition.php index 156d8fd5..33616650 100644 --- a/src/DataDefinition/Palette/Condition/Property/PropertyTrueCondition.php +++ b/src/DataDefinition/Palette/Condition/Property/PropertyTrueCondition.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -31,79 +32,7 @@ */ class PropertyTrueCondition implements PropertyConditionInterface { - /** - * The property name. - * - * @var string - */ - protected $propertyName; - - /** - * Use strict compare mode. - * - * @var bool - */ - protected $strict; - - /** - * Create a new instance. - * - * @param string $propertyName The name of the property. - * @param bool $strict Flag if the comparison shall be strict (type safe). - */ - public function __construct($propertyName = '', $strict = false) - { - $this->propertyName = (string) $propertyName; - $this->strict = (bool) $strict; - } - - /** - * Set the property name. - * - * @param string $propertyName The property name. - * - * @return PropertyTrueCondition - */ - public function setPropertyName($propertyName) - { - $this->propertyName = $propertyName; - return $this; - } - - /** - * Retrieve the property name. - * - * @return string - */ - public function getPropertyName() - { - return $this->propertyName; - } - - /** - * Set the flag if the comparison shall be strict (type safe). - * - * @param boolean $strict The flag. - * - * @return PropertyTrueCondition - */ - public function setStrict($strict) - { - $this->strict = $strict; - return $this; - } - - /** - * Retrieve the flag if the comparison shall be strict (type safe). - * - * @return boolean - * - * @SuppressWarnings(PHPMD.BooleanGetMethodName) - */ - public function getStrict() - { - return $this->strict; - } + use PropertyBooleanConditionTrait; /** * {@inheritdoc} @@ -124,11 +53,4 @@ public function match( return $this->strict ? ($value === true) : (bool) $value; } - - /** - * {@inheritdoc} - */ - public function __clone() - { - } } diff --git a/src/DataDefinition/Palette/Condition/Property/PropertyValueCondition.php b/src/DataDefinition/Palette/Condition/Property/PropertyValueCondition.php index e55fb4c0..a37c45dc 100644 --- a/src/DataDefinition/Palette/Condition/Property/PropertyValueCondition.php +++ b/src/DataDefinition/Palette/Condition/Property/PropertyValueCondition.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -61,9 +62,9 @@ class PropertyValueCondition implements PropertyConditionInterface */ public function __construct($propertyName = '', $propertyValue = null, $strict = false) { - $this->propertyName = (string) $propertyName; + $this->propertyName = $propertyName; $this->propertyValue = $propertyValue; - $this->strict = (bool) $strict; + $this->strict = $strict; } /** @@ -75,7 +76,8 @@ public function __construct($propertyName = '', $propertyValue = null, $strict = */ public function setPropertyName($propertyName) { - $this->propertyName = (string) $propertyName; + $this->propertyName = $propertyName; + return $this; } @@ -99,6 +101,7 @@ public function getPropertyName() public function setPropertyValue($propertyValue) { $this->propertyValue = $propertyValue; + return $this; } @@ -121,7 +124,8 @@ public function getPropertyValue() */ public function setStrict($strict) { - $this->strict = (bool) $strict; + $this->strict = $strict; + return $this; } diff --git a/src/DataDefinition/Palette/Condition/Property/PropertyVisibleCondition.php b/src/DataDefinition/Palette/Condition/Property/PropertyVisibleCondition.php index ebcba4dc..0f0ba035 100644 --- a/src/DataDefinition/Palette/Condition/Property/PropertyVisibleCondition.php +++ b/src/DataDefinition/Palette/Condition/Property/PropertyVisibleCondition.php @@ -45,7 +45,7 @@ class PropertyVisibleCondition implements PropertyConditionInterface */ public function __construct($propertyName = '') { - $this->propertyName = (string) $propertyName; + $this->propertyName = $propertyName; } /** @@ -57,7 +57,7 @@ public function __construct($propertyName = '') */ public function setPropertyName($propertyName) { - $this->propertyName = (string) $propertyName; + $this->propertyName = $propertyName; return $this; } @@ -84,8 +84,8 @@ public function match( return false; } - if ($legend->getPalette()) { - return $legend->getPalette()->getProperty($this->propertyName)->isVisible($model, $input, $legend); + if (null !== ($palette = $legend->getPalette())) { + return $palette->getProperty($this->propertyName)->isVisible($model, $input, $legend); } return $legend->getProperty($this->propertyName)->isVisible($model, $input, $legend); diff --git a/src/DataDefinition/Palette/Legend.php b/src/DataDefinition/Palette/Legend.php index 2d96ce05..3f4378e0 100644 --- a/src/DataDefinition/Palette/Legend.php +++ b/src/DataDefinition/Palette/Legend.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -22,6 +23,7 @@ namespace ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette; use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; +use ContaoCommunityAlliance\DcGeneral\Data\PropertyValueBag; use ContaoCommunityAlliance\DcGeneral\Data\PropertyValueBagInterface; use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralInvalidArgumentException; use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralRuntimeException; @@ -36,7 +38,7 @@ class Legend implements LegendInterface * * @var PaletteInterface|null */ - protected $palette; + protected $palette = null; /** * The name of this legend. @@ -55,7 +57,7 @@ class Legend implements LegendInterface /** * The properties in this legend. * - * @var PropertyInterface[]|array + * @var array */ protected $properties = []; @@ -95,7 +97,7 @@ public function getPalette() */ public function setName($name) { - $this->name = (string) $name; + $this->name = $name; return $this; } @@ -112,7 +114,7 @@ public function getName() */ public function setInitialVisibility($value) { - $this->initiallyVisible = (bool) $value; + $this->initiallyVisible = $value; return $this; } @@ -176,13 +178,15 @@ public function addProperty(PropertyInterface $property, PropertyInterface $befo ); } - $hashes = \array_keys($this->properties); - $position = \array_search($beforeHash, $hashes); + $hashes = \array_keys($this->properties); + if (false === ($position = \array_search($beforeHash, $hashes))) { + $position = null; + } $this->properties = \array_merge( \array_slice($this->properties, 0, $position), [$hash => $property], - \array_slice($this->properties, $position) + \array_slice($this->properties, $position ?? 0) ); return $this; @@ -212,7 +216,8 @@ public function getProperties(?ModelInterface $model = null, ?PropertyValueBagIn foreach ($this->properties as $property) { $condition = $property->getVisibleCondition(); - + // We should have defined the interfaces back in 2013... :/ + assert($input === null || $input instanceof PropertyValueBag); if (!$condition || $condition->match($model, $input, $property, $this)) { $selectedProperties[] = $property; } diff --git a/src/DataDefinition/Palette/Palette.php b/src/DataDefinition/Palette/Palette.php index 3f2f6115..bb559645 100644 --- a/src/DataDefinition/Palette/Palette.php +++ b/src/DataDefinition/Palette/Palette.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -37,12 +38,12 @@ class Palette implements PaletteInterface * * @var string */ - protected $name; + protected $name = ''; /** * List of all legends in this palette. * - * @var array|LegendInterface[] + * @var array */ protected $legends = []; @@ -58,7 +59,7 @@ class Palette implements PaletteInterface */ public function setName($name) { - $this->name = (string) $name; + $this->name = $name; return $this; } @@ -220,9 +221,9 @@ public function addLegend(LegendInterface $legend, LegendInterface $before = nul $position = \array_search($beforeHash, $hashes); $this->legends = \array_merge( - \array_slice($this->legends, 0, $position), + \array_slice($this->legends, 0, (int) $position), [$hash => $legend], - \array_slice($this->legends, $position) + \array_slice($this->legends, (int) $position) ); $legend->setPalette($this); diff --git a/src/DataDefinition/Palette/PaletteCollection.php b/src/DataDefinition/Palette/PaletteCollection.php index 9ee1b080..7227a9e8 100644 --- a/src/DataDefinition/Palette/PaletteCollection.php +++ b/src/DataDefinition/Palette/PaletteCollection.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2020 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2020 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -22,6 +23,7 @@ namespace ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette; use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; +use ContaoCommunityAlliance\DcGeneral\Data\PropertyValueBag; use ContaoCommunityAlliance\DcGeneral\Data\PropertyValueBagInterface; use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralInvalidArgumentException; @@ -33,7 +35,7 @@ class PaletteCollection implements PaletteCollectionInterface /** * The palettes contained in the collection. * - * @var array|PaletteInterface[] + * @var array */ protected $palettes = []; @@ -117,6 +119,8 @@ public function findPalette(ModelInterface $model = null, PropertyValueBagInterf $condition = $palette->getCondition(); if ($condition) { + // We should have defined the interfaces back in 2013... :/ + assert($input === null || $input instanceof PropertyValueBag); $count = $condition->getMatchCount($model, $input); if (false !== $count) { diff --git a/src/DataDefinition/Palette/PaletteCollectionInterface.php b/src/DataDefinition/Palette/PaletteCollectionInterface.php index cbe4cea7..a499b0a1 100644 --- a/src/DataDefinition/Palette/PaletteCollectionInterface.php +++ b/src/DataDefinition/Palette/PaletteCollectionInterface.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -22,7 +23,6 @@ namespace ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette; use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; -use ContaoCommunityAlliance\DcGeneral\Data\PropertyValueBag; use ContaoCommunityAlliance\DcGeneral\Data\PropertyValueBagInterface; /** @@ -33,7 +33,7 @@ interface PaletteCollectionInterface /** * Remove all palettes from this collection. * - * @return PaletteCollectionInterface + * @return self */ public function clearPalettes(); @@ -42,7 +42,7 @@ public function clearPalettes(); * * @param array|PaletteInterface[] $palettes The palettes. * - * @return PaletteCollectionInterface + * @return self */ public function setPalettes(array $palettes); @@ -51,7 +51,7 @@ public function setPalettes(array $palettes); * * @param array|PaletteInterface[] $palettes The palettes. * - * @return PaletteInterface + * @return self */ public function addPalettes(array $palettes); @@ -60,7 +60,7 @@ public function addPalettes(array $palettes); * * @param PaletteInterface $palette The palette. * - * @return PaletteInterface + * @return self */ public function addPalette(PaletteInterface $palette); @@ -69,7 +69,7 @@ public function addPalette(PaletteInterface $palette); * * @param PaletteInterface $palette The palette. * - * @return PaletteInterface + * @return self */ public function removePalette(PaletteInterface $palette); diff --git a/src/DataDefinition/Palette/Property.php b/src/DataDefinition/Palette/Property.php index 74ec4556..3b00cc9a 100644 --- a/src/DataDefinition/Palette/Property.php +++ b/src/DataDefinition/Palette/Property.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -23,6 +24,7 @@ use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; use ContaoCommunityAlliance\DcGeneral\Data\PropertyValueBag; +use ContaoCommunityAlliance\DcGeneral\Data\PropertyValueBagInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Condition\Property\PropertyConditionInterface; /** @@ -40,16 +42,16 @@ class Property implements PropertyInterface /** * The condition to be examined to determine if this property is visible. * - * @var PropertyConditionInterface + * @var PropertyConditionInterface|null */ - protected $visibleCondition; + protected $visibleCondition = null; /** * The condition to be examined to determine if this property is editable. * - * @var PropertyConditionInterface + * @var PropertyConditionInterface|null */ - protected $editableCondition; + protected $editableCondition = null; /** * Create a new instance. @@ -66,7 +68,9 @@ public function __construct($name) */ public function setName($name) { - $this->name = (string) $name; + $this->name = $name; + + return $this; } /** @@ -82,10 +86,12 @@ public function getName() */ public function isVisible( ModelInterface $model = null, - PropertyValueBag $input = null, + PropertyValueBagInterface $input = null, LegendInterface $legend = null ) { if ($this->visibleCondition) { + // We should have defined the interfaces back in 2013... :/ + assert($input === null || $input instanceof PropertyValueBag); return $this->visibleCondition->match($model, $input, $this, $legend); } @@ -97,10 +103,12 @@ public function isVisible( */ public function isEditable( ModelInterface $model = null, - PropertyValueBag $input = null, + PropertyValueBagInterface $input = null, LegendInterface $legend = null ) { if ($this->editableCondition) { + // We should have defined the interfaces back in 2013... :/ + assert($input === null || $input instanceof PropertyValueBag); return $this->editableCondition->match($model, $input, $this, $legend); } diff --git a/src/DataDefinition/Palette/PropertyInterface.php b/src/DataDefinition/Palette/PropertyInterface.php index 82896db7..e88b3bd2 100644 --- a/src/DataDefinition/Palette/PropertyInterface.php +++ b/src/DataDefinition/Palette/PropertyInterface.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -22,7 +23,7 @@ namespace ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette; use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; -use ContaoCommunityAlliance\DcGeneral\Data\PropertyValueBag; +use ContaoCommunityAlliance\DcGeneral\Data\PropertyValueBagInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Condition\Property\PropertyConditionInterface; /** @@ -49,38 +50,40 @@ public function getName(); /** * Check the conditions, if this property is visible. * - * @param ModelInterface|null $model If given, sub palettes will be evaluated depending on the model. - * If no model is given, all properties will be returned, including sub palette - * properties. - * @param PropertyValueBag $input If given, sub palettes will be evaluated depending on the input data. - * If no model and no input data is given, all properties will be returned, - * including sub palette properties. - * @param LegendInterface $legend The legend the property is assigned to. + * @param ModelInterface|null $model If given, sub palettes will be evaluated depending on the model. + * If no model is given, all properties will be returned, including + * sub palette properties. + * @param PropertyValueBagInterface|null $input If given, sub palettes will be evaluated depending on the input + * data. + * If no model and no input data is given, all properties will be + * returned, including sub palette properties. + * @param LegendInterface|null $legend The legend the property is assigned to. * * @return bool */ public function isVisible( ModelInterface $model = null, - PropertyValueBag $input = null, + PropertyValueBagInterface $input = null, LegendInterface $legend = null ); /** * Check the conditions, if this property is editable. * - * @param ModelInterface|null $model If given, sub palettes will be evaluated depending on the model. - * If no model is given, all properties will be returned, including sub palette - * properties. - * @param PropertyValueBag $input If given, sub palettes will be evaluated depending on the input data. - * If no model and no input data is given, all properties will be returned, - * including sub palette properties. - * @param LegendInterface $legend The legend the property is assigned to. + * @param ModelInterface|null $model If given, sub palettes will be evaluated depending on the model. + * If no model is given, all properties will be returned, including + * sub palette properties. + * @param PropertyValueBagInterface|null $input If given, sub palettes will be evaluated depending on the input + * data. + * If no model and no input data is given, all properties will be + * returned, including sub palette properties. + * @param LegendInterface $legend The legend the property is assigned to. * * @return bool */ public function isEditable( ModelInterface $model = null, - PropertyValueBag $input = null, + PropertyValueBagInterface $input = null, LegendInterface $legend = null ); @@ -96,7 +99,7 @@ public function setVisibleCondition(PropertyConditionInterface $condition = null /** * Get the visible condition for this property. * - * @return PropertyConditionInterface + * @return PropertyConditionInterface|null */ public function getVisibleCondition(); @@ -112,7 +115,7 @@ public function setEditableCondition(PropertyConditionInterface $condition = nul /** * Get the editable condition for this property. * - * @return PropertyConditionInterface + * @return PropertyConditionInterface|null */ public function getEditableCondition(); diff --git a/src/DataDefinitionContainer.php b/src/DataDefinitionContainer.php index e2fb1676..b45c1bba 100644 --- a/src/DataDefinitionContainer.php +++ b/src/DataDefinitionContainer.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,8 @@ * @package contao-community-alliance/dc-general * @author Christian Schiffler * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -33,16 +34,16 @@ class DataDefinitionContainer implements DataDefinitionContainerInterface /** * The definitions stored in the container. * - * @var ContainerInterface[] + * @var array */ - protected $definitions; + protected $definitions = []; /** * {@inheritDoc} */ public function setDefinition($name, $definition) { - if ($definition) { + if (null !== $definition) { $this->definitions[$name] = $definition; } else { unset($this->definitions[$name]); diff --git a/src/DataDefinitionContainerInterface.php b/src/DataDefinitionContainerInterface.php index 28861f7f..bd8e98d7 100644 --- a/src/DataDefinitionContainerInterface.php +++ b/src/DataDefinitionContainerInterface.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,8 @@ * @package contao-community-alliance/dc-general * @author Christian Schiffler * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -32,8 +33,8 @@ interface DataDefinitionContainerInterface /** * Add or override a definition in the container. * - * @param string $name Name of the definition. - * @param ContainerInterface $definition The definition to store. + * @param string $name Name of the definition. + * @param ContainerInterface|null $definition The definition to store - if null, the definition will get unset. * * @return DataDefinitionContainerInterface */ diff --git a/src/DefaultEnvironment.php b/src/DefaultEnvironment.php index b8e133ae..8dc39472 100644 --- a/src/DefaultEnvironment.php +++ b/src/DefaultEnvironment.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -271,7 +272,11 @@ public function getBaseConfigRegistry() public function hasDataProvider($source = null) { if (null === $source) { - $source = $this->getDataDefinition()->getBasicDefinition()->getDataProvider(); + $definition = $this->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $source = $definition->getBasicDefinition()->getDataProvider(); + assert(\is_string($source)); } return isset($this->arrDataProvider[$source]); @@ -282,28 +287,32 @@ public function hasDataProvider($source = null) * * @throws DcGeneralRuntimeException When an undefined provider is requested. */ - public function getDataProvider($source = null) + public function getDataProvider($strSource = null) { - if (null === $source) { - $source = $this->getDataDefinition()->getBasicDefinition()->getDataProvider(); + if (null === $strSource) { + $definition = $this->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $strSource = $definition->getBasicDefinition()->getDataProvider(); + assert(\is_string($strSource)); } - if (isset($this->arrDataProvider[$source])) { - return $this->arrDataProvider[$source]; + if (isset($this->arrDataProvider[$strSource])) { + return $this->arrDataProvider[$strSource]; } - throw new DcGeneralRuntimeException(\sprintf('Data provider %s not defined', $source)); + throw new DcGeneralRuntimeException(\sprintf('Data provider %s not defined', $strSource)); } /** * {@inheritdoc} */ - public function addDataProvider($source, $dataProvider) + public function addDataProvider($strSource, $dataProvider) { // Force removal of an potentially registered data provider to ease sub-classing. - $this->removeDataProvider($source); + $this->removeDataProvider($strSource); - $this->arrDataProvider[$source] = $dataProvider; + $this->arrDataProvider[$strSource] = $dataProvider; return $this; } @@ -311,10 +320,10 @@ public function addDataProvider($source, $dataProvider) /** * {@inheritdoc} */ - public function removeDataProvider($source) + public function removeDataProvider($strSource) { - if (isset($this->arrDataProvider[$source])) { - unset($this->arrDataProvider[$source]); + if (isset($this->arrDataProvider[$strSource])) { + unset($this->arrDataProvider[$strSource]); } return $this; @@ -331,13 +340,9 @@ public function getClipboard() /** * {@inheritdoc} */ - public function setClipboard($clipboard) + public function setClipboard($objClipboard) { - if (null === $clipboard) { - $this->objClipboard = null; - } else { - $this->objClipboard = $clipboard; - } + $this->objClipboard = $objClipboard; return $this; } @@ -345,13 +350,14 @@ public function setClipboard($clipboard) /** * {@inheritdoc} */ - public function setTranslator(TranslatorInterface $translator) + public function setTranslator(TranslatorInterface $manager) { - $this->translator = $translator; + $this->translator = $manager; return $this; } + /** * {@inheritdoc} */ diff --git a/src/DependencyInjection/CcaDcGeneralExtension.php b/src/DependencyInjection/CcaDcGeneralExtension.php index 21fc22bd..3c7b1d29 100644 --- a/src/DependencyInjection/CcaDcGeneralExtension.php +++ b/src/DependencyInjection/CcaDcGeneralExtension.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,8 @@ * @package contao-community-alliance/dc-general * @author Sven Baumann * @author Christian Schiffler - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -56,7 +57,7 @@ class CcaDcGeneralExtension extends Extension /** * {@inheritDoc} */ - public function load(array $configs, ContainerBuilder $container) + public function load(array $configs, ContainerBuilder $container): void { $loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config')); foreach ($this->files as $file) { diff --git a/src/DependencyInjection/Compiler/AddSessionBagsPass.php b/src/DependencyInjection/Compiler/AddSessionBagsPass.php index 6e4a44e6..e4d9c1d5 100644 --- a/src/DependencyInjection/Compiler/AddSessionBagsPass.php +++ b/src/DependencyInjection/Compiler/AddSessionBagsPass.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -12,7 +12,8 @@ * * @package contao-community-alliance/dc-general * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -31,7 +32,7 @@ class AddSessionBagsPass implements CompilerPassInterface /** * {@inheritdoc} */ - public function process(ContainerBuilder $container) + public function process(ContainerBuilder $container): void { if (!$container->has('session')) { return; diff --git a/src/EnvironmentInterface.php b/src/EnvironmentInterface.php index 32d0e9fd..bab50793 100644 --- a/src/EnvironmentInterface.php +++ b/src/EnvironmentInterface.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -41,11 +42,11 @@ interface EnvironmentInterface /** * Set the Controller for the current setup. * - * @param ControllerInterface $objController The controller to use. + * @param ControllerInterface $controller The controller to use. * * @return EnvironmentInterface */ - public function setController($objController); + public function setController($controller); /** * Retrieve the Controller from the current setup. @@ -57,11 +58,11 @@ public function getController(); /** * Set the View for the current setup. * - * @param ViewInterface $objView The view to use. + * @param ViewInterface $view The view to use. * * @return EnvironmentInterface */ - public function setView($objView); + public function setView($view); /** * Retrieve the View from the current setup. @@ -73,11 +74,11 @@ public function getView(); /** * Set the data definition for this instance. * - * @param ContainerInterface $objContainer The data definition container to store. + * @param ContainerInterface $dataDefinition The data definition container to store. * * @return EnvironmentInterface */ - public function setDataDefinition($objContainer); + public function setDataDefinition($dataDefinition); /** * Retrieve the data definition for this instance. @@ -89,11 +90,13 @@ public function getDataDefinition(); /** * Set the data definition of the parent container. * - * @param ContainerInterface $objContainer The data definition container to store. + * @param ContainerInterface $objParentDataDefinition The data definition container to store. * * @return EnvironmentInterface + * + * @SuppressWarnings(PHPMD.LongVariable) */ - public function setParentDataDefinition($objContainer); + public function setParentDataDefinition($objParentDataDefinition); /** * Retrieve the data definition for the parent container. This applies only when in parented mode. @@ -105,11 +108,11 @@ public function getParentDataDefinition(); /** * Set the data definition of the root container. * - * @param ContainerInterface $objContainer The data definition container to store. + * @param ContainerInterface $rootDataDefinition The data definition container to store. * * @return EnvironmentInterface */ - public function setRootDataDefinition($objContainer); + public function setRootDataDefinition($rootDataDefinition); /** * Retrieve the data definition for the root container. This applies only when in hierarchical mode. @@ -137,11 +140,11 @@ public function getSessionStorage(); /** * Set the input provider to use. * - * @param InputProviderInterface $objInputProvider The input provider to use. + * @param InputProviderInterface $inputProvider The input provider to use. * * @return EnvironmentInterface */ - public function setInputProvider($objInputProvider); + public function setInputProvider($inputProvider); /** * Retrieve the input provider. @@ -169,11 +172,11 @@ public function getBaseConfigRegistry(); /** * Determine if the data provider with the given name exists. * - * @param string|null $strSource The source name to check the providers for. + * @param string|null $source The source name to check the providers for. * * @return mixed */ - public function hasDataProvider($strSource = null); + public function hasDataProvider($source = null); /** * Retrieve the data provider for the named source. @@ -214,9 +217,9 @@ public function removeDataProvider($strSource); public function getClipboard(); /** - * Set the the clipboard. + * Set the clipboard. * - * @param ClipboardInterface $objClipboard Clipboard instance. + * @param ClipboardInterface|null $objClipboard Clipboard instance. * * @return EnvironmentInterface */ diff --git a/src/EnvironmentPopulator/AbstractEventDrivenEnvironmentPopulator.php b/src/EnvironmentPopulator/AbstractEventDrivenEnvironmentPopulator.php index cd169618..2c513f91 100644 --- a/src/EnvironmentPopulator/AbstractEventDrivenEnvironmentPopulator.php +++ b/src/EnvironmentPopulator/AbstractEventDrivenEnvironmentPopulator.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,8 @@ * @package contao-community-alliance/dc-general * @author Christian Schiffler * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -50,8 +51,9 @@ abstract class AbstractEventDrivenEnvironmentPopulator implements EnvironmentPop */ public static function process(PopulateEnvironmentEvent $event) { + /** @psalm-suppress UnsafeInstantiation */ $builder = new static(); - /** @var $builder EnvironmentPopulatorInterface */ + /** @var EnvironmentPopulatorInterface $builder */ $builder->populate($event->getEnvironment()); } } diff --git a/src/EventListener/ModelRelationship/ParentEnforcingListener.php b/src/EventListener/ModelRelationship/ParentEnforcingListener.php index 59e0d5df..b35bc3f1 100644 --- a/src/EventListener/ModelRelationship/ParentEnforcingListener.php +++ b/src/EventListener/ModelRelationship/ParentEnforcingListener.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,8 @@ * @package contao-community-alliance/dc-general * @author Christian Schiffler * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -23,8 +24,11 @@ use ContaoCommunityAlliance\DcGeneral\Controller\ModelCollector; use ContaoCommunityAlliance\DcGeneral\Controller\RelationshipManager; use ContaoCommunityAlliance\DcGeneral\Data\ModelId; +use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\BasicDefinitionInterface; use ContaoCommunityAlliance\DcGeneral\Event\EnforceModelRelationshipEvent; +use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; /** * This class takes care of enforcing a parent child relationship on a model. @@ -41,9 +45,14 @@ class ParentEnforcingListener public function process(EnforceModelRelationshipEvent $event) { $environment = $event->getEnvironment(); + $definition = $environment->getDataDefinition(); - $mode = $definition->getBasicDefinition()->getMode(); - $input = $environment->getInputProvider(); + assert($definition instanceof ContainerInterface); + + $mode = $definition->getBasicDefinition()->getMode(); + + $input = $environment->getInputProvider(); + assert($input instanceof InputProviderInterface); if (BasicDefinitionInterface::MODE_PARENTEDLIST !== $mode || !$input->hasParameter('pid')) { return; @@ -52,6 +61,7 @@ public function process(EnforceModelRelationshipEvent $event) $model = $event->getModel(); $parent = (new ModelCollector($environment))->getModel(ModelId::fromSerialized($input->getParameter('pid'))); + assert($parent instanceof ModelInterface); (new RelationshipManager($definition->getModelRelationshipDefinition(), $mode))->setParent($model, $parent); } diff --git a/src/EventListener/ModelRelationship/TreeEnforcingListener.php b/src/EventListener/ModelRelationship/TreeEnforcingListener.php index 3fae9e25..12cccae2 100644 --- a/src/EventListener/ModelRelationship/TreeEnforcingListener.php +++ b/src/EventListener/ModelRelationship/TreeEnforcingListener.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Sven Baumann * @author Richard Henkenjohann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -24,9 +25,12 @@ use ContaoCommunityAlliance\DcGeneral\Controller\ModelCollector; use ContaoCommunityAlliance\DcGeneral\Controller\RelationshipManager; use ContaoCommunityAlliance\DcGeneral\Data\ModelId; +use ContaoCommunityAlliance\DcGeneral\Data\ModelIdInterface; use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\BasicDefinitionInterface; use ContaoCommunityAlliance\DcGeneral\Event\EnforceModelRelationshipEvent; +use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; /** * This class takes care of enforcing a tree relationship on a model. @@ -43,17 +47,23 @@ class TreeEnforcingListener public function process(EnforceModelRelationshipEvent $event) { $environment = $event->getEnvironment(); - $mode = $environment->getDataDefinition()->getBasicDefinition()->getMode(); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $mode = $definition->getBasicDefinition()->getMode(); if (BasicDefinitionInterface::MODE_HIERARCHICAL !== $mode) { return; } - $input = $environment->getInputProvider(); + $input = $environment->getInputProvider(); + assert($input instanceof InputProviderInterface); + $model = $event->getModel(); $collector = new ModelCollector($environment); $relationships = new RelationshipManager( - $environment->getDataDefinition()->getModelRelationshipDefinition(), + $definition->getModelRelationshipDefinition(), $mode ); @@ -76,6 +86,7 @@ public function process(EnforceModelRelationshipEvent $event) // Also enforce the parent condition of the parent provider (if any). if ($input->hasParameter('pid')) { $parent = $collector->getModel($input->getParameter('pid')); + assert($parent instanceof ModelInterface); $relationships->setParent($model, $parent); } } @@ -83,7 +94,7 @@ public function process(EnforceModelRelationshipEvent $event) /** * Handle paste into. * - * @param ModelId $into The id of the new parenting model. + * @param ModelIdInterface $into The id of the new parenting model. * @param RelationshipManager $relationships The relationship manager. * @param ModelCollector $collector The model collector. * @param ModelInterface $model The model. @@ -91,11 +102,11 @@ public function process(EnforceModelRelationshipEvent $event) * @return void */ private function handleInto( - ModelId $into, + ModelIdInterface $into, RelationshipManager $relationships, ModelCollector $collector, ModelInterface $model - ) { + ): void { // If we have a null, it means insert into the tree root. if (0 === $into->getId() || null === $parent = $collector->getModel($into)) { $relationships->setRoot($model); @@ -108,7 +119,7 @@ private function handleInto( /** * Handle paste after. * - * @param ModelId $after The id of the sibling model. + * @param ModelIdInterface $after The id of the sibling model. * @param RelationshipManager $relationships The relationship manager. * @param ModelCollector $collector The model collector. * @param ModelInterface $model The model. @@ -116,7 +127,7 @@ private function handleInto( * @return void */ private function handleAfter( - ModelId $after, + ModelIdInterface $after, RelationshipManager $relationships, ModelCollector $collector, ModelInterface $model @@ -129,6 +140,8 @@ private function handleAfter( } $parent = $collector->searchParentOf($sibling); + assert($parent instanceof ModelInterface); + $relationships->setParent($model, $parent); } } diff --git a/src/Factory/DcGeneralFactory.php b/src/Factory/DcGeneralFactory.php index 1fe0e6f7..519fd3df 100644 --- a/src/Factory/DcGeneralFactory.php +++ b/src/Factory/DcGeneralFactory.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2022 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -15,7 +15,7 @@ * @author Tristan Lins * @author Sven Baumann * @author Ingolf Steinhardt - * @copyright 2013-2022 Contao Community Alliance. + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -49,9 +49,9 @@ class DcGeneralFactory implements DcGeneralFactoryInterface /** * The cache. * - * @var CacheInterface|null + * @var CacheInterface */ - private $cache; + private CacheInterface $cache; /** * The constructor. @@ -60,16 +60,19 @@ class DcGeneralFactory implements DcGeneralFactoryInterface */ public function __construct(CacheInterface $cache = null) { - $this->cache = $cache; - if (null === $this->cache) { + if (null === $cache) { // @codingStandardsIgnoreStart @\trigger_error( - 'You should use ' . DcGeneralFactoryCache::class . ' .', + 'You should pass an instance of ' . CacheInterface::class . ' .', E_USER_DEPRECATED ); // @codingStandardsIgnoreEnd - $this->cache = System::getContainer()->get(DcGeneralFactoryCache::class); + /** @psalm-suppress DeprecatedClass */ + $cache = System::getContainer()->get(DcGeneralFactoryCache::class); + + assert($cache instanceof CacheInterface); } + $this->cache = $cache; } /** @@ -84,11 +87,20 @@ public function __construct(CacheInterface $cache = null) */ public static function deriveEmptyFromEnvironment(EnvironmentInterface $environment) { + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $translator = $environment->getTranslator(); + assert($translator instanceof TranslatorInterface); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + $factory = new DcGeneralFactory(); - $factory->setEventDispatcher($environment->getEventDispatcher()); - $factory->setTranslator($environment->getTranslator()); + $factory->setEventDispatcher($dispatcher); + $factory->setTranslator($translator); $factory->setEnvironmentClassName(\get_class($environment)); - $factory->setContainerClassName(\get_class($environment->getDataDefinition())); + $factory->setContainerClassName(\get_class($definition)); return $factory; } @@ -104,14 +116,18 @@ public static function deriveEmptyFromEnvironment(EnvironmentInterface $environm public static function deriveFromEnvironment(EnvironmentInterface $environment) { $factory = static::deriveEmptyFromEnvironment($environment); - $factory->setContainerName($environment->getDataDefinition()->getName()); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $factory->setContainerName($definition->getName()); return $factory; } /** * The class name to use for the environment. * - * @var string + * @var class-string */ protected $environmentClassName = DefaultEnvironment::class; @@ -120,56 +136,56 @@ public static function deriveFromEnvironment(EnvironmentInterface $environment) * * @var string */ - protected $containerName; + protected $containerName = ''; /** * The class name of the class to use for the data definition container. * - * @var string + * @var class-string */ protected $containerClassName = DefaultContainer::class; /** * The class name of the class to use as DcGeneral. * - * @var string + * @var class-string */ protected $dcGeneralClassName = DcGeneral::class; /** * The event dispatcher to use. * - * @var EventDispatcherInterface + * @var EventDispatcherInterface|null */ - protected $eventDispatcher; + protected $eventDispatcher = null; /** * The translator that shall be used. * - * @var TranslatorInterface + * @var TranslatorInterface|null */ - protected $translator; + protected $translator = null; /** * The environment for the new instance. * - * @var EnvironmentInterface + * @var EnvironmentInterface|null */ - protected $environment; + protected $environment = null; /** * The data definition container instance. * - * @var ContainerInterface + * @var ContainerInterface|null */ - protected $dataContainer; + protected $dataContainer = null; /** * {@inheritdoc} */ public function setEnvironmentClassName($environmentClassName) { - $this->environmentClassName = (string) $environmentClassName; + $this->environmentClassName = $environmentClassName; return $this; } @@ -187,7 +203,7 @@ public function getEnvironmentClassName() */ public function setContainerName($containerName) { - $this->containerName = (string) $containerName; + $this->containerName = $containerName; return $this; } @@ -205,7 +221,7 @@ public function getContainerName() */ public function setContainerClassName($containerClassName) { - $this->containerClassName = (string) $containerClassName; + $this->containerClassName = $containerClassName; return $this; } @@ -223,7 +239,7 @@ public function getContainerClassName() */ public function setDcGeneralClassName($dcGeneralClassName) { - $this->dcGeneralClassName = (string) $dcGeneralClassName; + $this->dcGeneralClassName = $dcGeneralClassName; return $this; } @@ -251,6 +267,10 @@ public function setEventDispatcher($dispatcher) */ public function getEventDispatcher() { + if (null === $this->eventDispatcher) { + throw new DcGeneralRuntimeException('Required event dispatcher is missing'); + } + return $this->eventDispatcher; } @@ -269,6 +289,10 @@ public function setTranslator(TranslatorInterface $translator) */ public function getTranslator() { + if (null === $this->translator) { + throw new DcGeneralRuntimeException('Required translator is missing'); + } + return $this->translator; } @@ -324,13 +348,15 @@ public function createDcGeneral() } $cacheKey = \md5('dc-general.' . $this->containerName); - return $this->cache->get($cacheKey, function (): DcGeneral { + $cache = $this->cache; + assert($cache instanceof CacheInterface); + + return $cache->get($cacheKey, function (): DcGeneral { // Backwards compatibility. $this->getEventDispatcher()->dispatch(new PreCreateDcGeneralEvent($this), PreCreateDcGeneralEvent::NAME); $environment = $this->environment ?: $this->createEnvironment(); - /** @var DcGeneral $dcGeneral */ $dcGeneral = (new \ReflectionClass($this->dcGeneralClassName))->newInstance($environment); // Backwards compatibility. @@ -352,25 +378,16 @@ public function createEnvironment() throw new DcGeneralRuntimeException('Required container name or container is missing'); } - if (null === $this->eventDispatcher) { - throw new DcGeneralRuntimeException('Required event dispatcher is missing'); - } - - if (null === $this->translator) { - throw new DcGeneralRuntimeException('Required translator is missing'); - } - if ($this->dataContainer) { $dataContainer = clone $this->dataContainer; } else { $dataContainer = $this->createContainer(); } - /** @var EnvironmentInterface $environment */ $environment = (new \ReflectionClass($this->environmentClassName))->newInstance(); $environment->setDataDefinition($dataContainer); - $environment->setEventDispatcher($this->eventDispatcher); - $environment->setTranslator($this->translator); + $environment->setEventDispatcher($this->getEventDispatcher()); + $environment->setTranslator($this->getTranslator()); // Backwards compatibility. $this->getEventDispatcher()->dispatch( @@ -395,12 +412,9 @@ public function createContainer() throw new DcGeneralRuntimeException('Required container name is missing'); } - if (null === $this->eventDispatcher) { - throw new DcGeneralRuntimeException('Required event dispatcher is missing'); - } - /** @var DataDefinitionContainerInterface $definitions */ $definitions = System::getContainer()->get('cca.dc-general.data-definition-container'); + assert($definitions instanceof DataDefinitionContainerInterface); if ($definitions->hasDefinition($this->containerName)) { return clone $definitions->getDefinition($this->containerName); diff --git a/src/Factory/DcGeneralFactoryInterface.php b/src/Factory/DcGeneralFactoryInterface.php index 9ee9b345..037d1e3b 100644 --- a/src/Factory/DcGeneralFactoryInterface.php +++ b/src/Factory/DcGeneralFactoryInterface.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -37,7 +38,7 @@ interface DcGeneralFactoryInterface /** * Set the class name to use as environment. * - * @param string $environmentClassName The class name. + * @param class-string $environmentClassName The class name. * * @return DcGeneralFactoryInterface */ @@ -46,7 +47,7 @@ public function setEnvironmentClassName($environmentClassName); /** * Retrieve the class name to use as environment. * - * @return string + * @return class-string */ public function getEnvironmentClassName(); @@ -69,7 +70,7 @@ public function getContainerName(); /** * Set the class name to use as data definition container. * - * @param string $containerClassName The class name. + * @param class-string $containerClassName The class name. * * @return DcGeneralFactoryInterface */ @@ -78,14 +79,14 @@ public function setContainerClassName($containerClassName); /** * Retrieve the class name to use as data definition container. * - * @return string + * @return class-string */ public function getContainerClassName(); /** * Set the class name to use as DcGeneral. * - * @param string $dcGeneralClassName The class name. + * @param class-string $dcGeneralClassName The class name. * * @return DcGeneralFactoryInterface */ @@ -94,7 +95,7 @@ public function setDcGeneralClassName($dcGeneralClassName); /** * Retrieve the class name to use as DcGeneral. * - * @return string + * @return class-string */ public function getDcGeneralClassName(); @@ -133,32 +134,32 @@ public function getTranslator(); /** * Set the environment to use. * - * @param EnvironmentInterface $environment The environment instance. + * @param EnvironmentInterface|null $environment The environment instance. * * @return DcGeneralFactoryInterface */ - public function setEnvironment(EnvironmentInterface $environment = null); + public function setEnvironment(?EnvironmentInterface $environment = null); /** * Retrieve the environment to use. * - * @return EnvironmentInterface + * @return EnvironmentInterface|null */ public function getEnvironment(); /** * Set the data definition container to use. * - * @param ContainerInterface $dataContainer The data definition container instance. + * @param ContainerInterface|null $dataContainer The data definition container instance. * * @return DcGeneralFactoryInterface */ - public function setDataContainer(ContainerInterface $dataContainer = null); + public function setDataContainer(?ContainerInterface $dataContainer = null); /** * Retrieve the data definition container. * - * @return ContainerInterface + * @return ContainerInterface|null */ public function getDataContainer(); diff --git a/src/Panel/AbstractElement.php b/src/Panel/AbstractElement.php index 41ac490f..10ffd98a 100644 --- a/src/Panel/AbstractElement.php +++ b/src/Panel/AbstractElement.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,13 +14,15 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ namespace ContaoCommunityAlliance\DcGeneral\Panel; +use ContaoCommunityAlliance\DcGeneral\BaseConfigRegistryInterface; use ContaoCommunityAlliance\DcGeneral\Data\ConfigInterface; use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; @@ -34,18 +36,18 @@ abstract class AbstractElement implements PanelElementInterface /** * The panel this element is contained within. * - * @var PanelInterface + * @var PanelInterface|null */ - protected $objPanel; + protected $objPanel = null; /** * The base configuration that contains all filter, sorting and limit information for all other panel elements. * * This is used for determining the valid values in filters etc. * - * @var ConfigInterface + * @var ConfigInterface|null */ - private $objOtherConfig; + private $objOtherConfig = null; /** * Convenience method to retrieve Environment for this element. @@ -64,7 +66,10 @@ public function getEnvironment() */ public function getSessionStorage() { - return $this->getEnvironment()->getSessionStorage(); + $sessionStorage = $this->getEnvironment()->getSessionStorage(); + assert($sessionStorage instanceof SessionStorageInterface); + + return $sessionStorage; } /** @@ -74,7 +79,10 @@ public function getSessionStorage() */ public function getInputProvider() { - return $this->getEnvironment()->getInputProvider(); + $inputProvider = $this->getEnvironment()->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + + return $inputProvider; } /** @@ -82,15 +90,19 @@ public function getInputProvider() */ public function getPanel() { + if (null === $this->objPanel) { + throw new \LogicException('Panel not set'); + } + return $this->objPanel; } /** * {@inheritDoc} */ - public function setPanel(PanelInterface $panel) + public function setPanel(PanelInterface $panelElement) { - $this->objPanel = $panel; + $this->objPanel = $panelElement; return $this; } @@ -102,11 +114,11 @@ public function setPanel(PanelInterface $panel) */ protected function getOtherConfig() { - if (!isset($this->objOtherConfig)) { - $this->objOtherConfig = $this - ->getEnvironment() - ->getBaseConfigRegistry() - ->getBaseConfig(); + if (null === $this->objOtherConfig) { + $registry = $this->getEnvironment()->getBaseConfigRegistry(); + assert($registry instanceof BaseConfigRegistryInterface); + + $this->objOtherConfig = $registry->getBaseConfig(); $this ->getPanel() diff --git a/src/Panel/DefaultFilterElement.php b/src/Panel/DefaultFilterElement.php index 9409a03d..2509cc89 100644 --- a/src/Panel/DefaultFilterElement.php +++ b/src/Panel/DefaultFilterElement.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2022 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -17,7 +17,7 @@ * @author Ingolf Steinhardt * @author Sven Baumann * @author Cliff Parnitzky - * @copyright 2013-2022 Contao Community Alliance. + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -25,7 +25,11 @@ namespace ContaoCommunityAlliance\DcGeneral\Panel; use ContaoCommunityAlliance\DcGeneral\Data\ConfigInterface; +use ContaoCommunityAlliance\DcGeneral\Data\DataProviderInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\ModelRelationship\FilterBuilder; +use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; +use ContaoCommunityAlliance\DcGeneral\SessionStorageInterface; use ContaoCommunityAlliance\DcGeneral\View\ViewTemplateInterface; /** @@ -38,7 +42,7 @@ class DefaultFilterElement extends AbstractElement implements FilterElementInter * * @var string */ - private $strProperty; + private $strProperty = 'string'; /** * The current value of this filter. @@ -52,7 +56,7 @@ class DefaultFilterElement extends AbstractElement implements FilterElementInter * * @var array */ - private $arrFilterOptions; + private $arrFilterOptions = []; /** * Retrieve the persistent value from the input provider. @@ -66,8 +70,11 @@ protected function getPersistent() $values = $this->getSessionStorage()->get('filter'); } - if (\array_key_exists($this->getEnvironment()->getDataDefinition()->getName(), $values)) { - $values = $values[$this->getEnvironment()->getDataDefinition()->getName()]; + $definition = $this->getEnvironment()->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + if (\array_key_exists($definition->getName(), $values)) { + $values = $values[$definition->getName()]; if (\array_key_exists($this->getPropertyName(), $values)) { return $values[$this->getPropertyName()]; @@ -86,7 +93,10 @@ protected function getPersistent() */ protected function setPersistent($value) { - $definitionName = $this->getEnvironment()->getDataDefinition()->getName(); + $definition = $this->getEnvironment()->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $definitionName = $definition->getName(); $values = []; @@ -95,7 +105,7 @@ protected function setPersistent($value) } if (isset($values[$definitionName]) && !\is_array($values[$definitionName])) { - $values[$this->getEnvironment()->getDataDefinition()->getName()] = []; + $values[$definition->getName()] = []; } if ((null !== $values) && ($value !== 'tl_' . $this->getPropertyName())) { @@ -115,10 +125,14 @@ protected function setPersistent($value) private function updateValue() { $session = $this->getSessionStorage(); - $input = $this->getInputProvider(); + assert($session instanceof SessionStorageInterface); + + $input = $this->getInputProvider(); + assert($input instanceof InputProviderInterface); + $value = null; - if ('1' !== $this->getEnvironment()->getInputProvider()->getValue('filter_reset')) { + if ('1' !== $input->getValue('filter_reset')) { if ($input->hasValue($this->getPropertyName()) && $this->getPanel()->getContainer()->updateValues()) { $value = $input->getValue($this->getPropertyName()); @@ -148,14 +162,14 @@ private function loadFilterOptions() $otherConfig = $this->getOtherConfig(); $otherConfig->setFields([$this->getPropertyName()]); - $filterOptions = $this - ->getEnvironment() - ->getDataProvider() - ->getFilterOptions($otherConfig); + $dataProvider = $this->getEnvironment()->getDataProvider(); + assert($dataProvider instanceof DataProviderInterface); + + $filterOptions = $dataProvider->getFilterOptions($otherConfig); $options = []; foreach ($filterOptions as $filterKey => $filterValue) { - $options[(string) $filterKey] = $filterValue; + $options[$filterKey] = $filterValue; } $this->arrFilterOptions = $options; @@ -193,10 +207,10 @@ public function initialize(ConfigInterface $config, PanelElementInterface $eleme */ public function render(ViewTemplateInterface $viewTemplate) { - $labels = $this - ->getEnvironment() - ->getDataDefinition() - ->getPropertiesDefinition() + $definition = $this->getEnvironment()->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $labels = $definition->getPropertiesDefinition() ->getProperty($this->getPropertyName())->getLabel(); $options = [ @@ -216,7 +230,7 @@ public function render(ViewTemplateInterface $viewTemplate) ]; } - $viewTemplate->set('label', (\is_array($labels) ? $labels[0] : $labels)); + $viewTemplate->set('label', $labels); $viewTemplate->set('name', $this->getPropertyName()); $viewTemplate->set('id', $this->getPropertyName()); $viewTemplate->set('class', 'tl_select' . ((null !== $selectedValue) ? ' active' : '')); diff --git a/src/Panel/DefaultLimitElement.php b/src/Panel/DefaultLimitElement.php index fb513b21..8d939278 100644 --- a/src/Panel/DefaultLimitElement.php +++ b/src/Panel/DefaultLimitElement.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2022 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -17,7 +17,7 @@ * @author Ingolf Steinhardt * @author Sven Baumann * @author Cliff Parnitzky - * @copyright 2013-2022 Contao Community Alliance. + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -26,6 +26,9 @@ use Contao\Config; use ContaoCommunityAlliance\DcGeneral\Data\ConfigInterface; +use ContaoCommunityAlliance\DcGeneral\Data\DataProviderInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; +use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; use ContaoCommunityAlliance\DcGeneral\View\ViewTemplateInterface; /** @@ -38,21 +41,21 @@ class DefaultLimitElement extends AbstractElement implements LimitElementInterfa * * @var int */ - private $intOffset; + private $intOffset = 0; /** * The current amount. * * @var int */ - private $intAmount; + private $intAmount = 0; /** * The total amount of all valid entries. * * @var int */ - private $intTotal; + private $intTotal = 0; /** * Retrieve the amount of items to display per page. @@ -82,24 +85,19 @@ protected function getMaxItemsPerPage() protected function calculateTotal() { $otherConfig = $this->getOtherConfig(); - $total = $this - ->getEnvironment() - ->getDataProvider() - ->fetchAll($otherConfig->setIdOnly(true)); - if (\is_array($total)) { - $this->intTotal = $total ? \count($total) : 0; + $dataProvider = $this->getEnvironment()->getDataProvider(); + assert($dataProvider instanceof DataProviderInterface); - return; - } + $total = $dataProvider->fetchAll($otherConfig->setIdOnly(true)); - if (\is_object($total)) { - $this->intTotal = $total->length(); + if (\is_array($total)) { + $this->intTotal = $total ? \count($total) : 0; return; } - $this->intTotal = 0; + $this->intTotal = $total->length(); } /** @@ -114,8 +112,11 @@ protected function getPersistent() $values = $this->getSessionStorage()->get('limit'); } - if (\array_key_exists($this->getEnvironment()->getDataDefinition()->getName(), $values)) { - return $values[$this->getEnvironment()->getDataDefinition()->getName()]; + $definition = $this->getEnvironment()->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + if (\array_key_exists($definition->getName(), $values)) { + return $values[$definition->getName()]; } return []; @@ -131,7 +132,10 @@ protected function getPersistent() */ protected function setPersistent($offset, $amount) { - $definitionName = $this->getEnvironment()->getDataDefinition()->getName(); + $definition = $this->getEnvironment()->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $definitionName = $definition->getName(); $values = []; @@ -172,11 +176,6 @@ public function initialize(ConfigInterface $config, PanelElementInterface $eleme $this->defineOffsetAndAmountOption($offset, $amount); - if (null !== $offset) { - $this->setOffset($offset); - $this->setAmount($amount); - } - $config->setStart($this->getOffset()); $config->setAmount($this->getAmount()); } @@ -191,8 +190,11 @@ public function initialize(ConfigInterface $config, PanelElementInterface $eleme */ private function defineOffsetAndAmountOption(int &$offset, int &$amount): void { - if ('1' === $this->getEnvironment()->getInputProvider()->getValue('filter_reset')) { - $this->setPersistent(null, null); + $inputProvider = $this->getEnvironment()->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + + if ('1' === $inputProvider->getValue('filter_reset')) { + $this->setPersistent(0, 0); return; } @@ -200,8 +202,8 @@ private function defineOffsetAndAmountOption(int &$offset, int &$amount): void if ($input->hasValue('tl_limit') && $this->getPanel()->getContainer()->updateValues()) { $limit = $input->getValue('tl_limit'); if ('tl_limit' !== $limit) { - [$offset, $amount] = \explode(',', $input->getValue('tl_limit')) + [null, null]; - $this->setPersistent($offset, $amount); + [$offset, $amount] = \explode(',', $input->getValue('tl_limit')) + [0, 0]; + $this->setPersistent((int) $offset, (int) $amount); } } @@ -295,9 +297,9 @@ public function render(ViewTemplateInterface $viewTemplate) /** * {@inheritDoc} */ - public function setOffset($offset) + public function setOffset($intOffset) { - $this->intOffset = (int) $offset; + $this->intOffset = $intOffset; return $this; } diff --git a/src/Panel/DefaultPanel.php b/src/Panel/DefaultPanel.php index 84506985..2db61511 100644 --- a/src/Panel/DefaultPanel.php +++ b/src/Panel/DefaultPanel.php @@ -32,16 +32,16 @@ class DefaultPanel implements PanelInterface /** * The panel container this panel is contained within. * - * @var PanelContainerInterface + * @var PanelContainerInterface|null */ - private $objContainer; + private ?PanelContainerInterface $objContainer = null; /** * The elements contained within this panel. * - * @var PanelElementInterface[] + * @var array */ - private $arrElements; + private array $arrElements; /** * Create a new instance. @@ -56,6 +56,10 @@ public function __construct() */ public function getContainer() { + if (null === $this->objContainer) { + throw new \LogicException('Container for panel is not set.'); + } + return $this->objContainer; } diff --git a/src/Panel/DefaultPanelContainer.php b/src/Panel/DefaultPanelContainer.php index 71ae322a..0643f846 100644 --- a/src/Panel/DefaultPanelContainer.php +++ b/src/Panel/DefaultPanelContainer.php @@ -24,6 +24,7 @@ use ContaoCommunityAlliance\DcGeneral\Data\ConfigInterface; use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; +use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; /** * Default implementation of a panel container. @@ -33,31 +34,36 @@ class DefaultPanelContainer implements PanelContainerInterface /** * The environment in use. * - * @var EnvironmentInterface + * @var EnvironmentInterface|null */ - private $objEnvironment; + private $objEnvironment = null; /** * The panels contained within this container. * - * @var PanelInterface[] + * @var array */ - private $arrPanels = []; + private array $panels = []; /** * {@inheritdoc} */ public function getEnvironment() { + if (null === $this->objEnvironment) { + throw new \LogicException('Environment for panel is not set.'); + } + return $this->objEnvironment; } /** * {@inheritdoc} */ - public function setEnvironment(EnvironmentInterface $environment) + public function setEnvironment(EnvironmentInterface $objEnvironment) { - $this->objEnvironment = $environment; + $this->objEnvironment = $objEnvironment; + return $this; } @@ -66,7 +72,7 @@ public function setEnvironment(EnvironmentInterface $environment) */ public function addPanel($panelName, $panel) { - $this->arrPanels[$panelName] = $panel; + $this->panels[$panelName] = $panel; $panel->setContainer($this); return $this; @@ -77,7 +83,7 @@ public function addPanel($panelName, $panel) */ public function getPanel($panelName) { - return $this->arrPanels[$panelName]; + return $this->panels[$panelName]; } /** @@ -89,6 +95,8 @@ public function initialize(ConfigInterface $config, PanelElementInterface $eleme foreach ($this as $panel) { $panel->initialize($config, $element); } + + return $this; } /** @@ -96,7 +104,10 @@ public function initialize(ConfigInterface $config, PanelElementInterface $eleme */ public function updateValues() { - return ('tl_filters' === $this->getEnvironment()->getInputProvider()->getValue('FORM_SUBMIT')); + $inputProvider = $this->getEnvironment()->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + + return ('tl_filters' === $inputProvider->getValue('FORM_SUBMIT')); } /** @@ -104,7 +115,7 @@ public function updateValues() */ public function getIterator(): \Traversable { - return new \ArrayIterator($this->arrPanels); + return new \ArrayIterator($this->panels); } /** @@ -112,6 +123,6 @@ public function getIterator(): \Traversable */ public function count(): int { - return \count($this->arrPanels); + return \count($this->panels); } } diff --git a/src/Panel/DefaultSearchElement.php b/src/Panel/DefaultSearchElement.php index 5f11d57a..72f3d2dd 100644 --- a/src/Panel/DefaultSearchElement.php +++ b/src/Panel/DefaultSearchElement.php @@ -24,6 +24,8 @@ namespace ContaoCommunityAlliance\DcGeneral\Panel; use ContaoCommunityAlliance\DcGeneral\Data\ConfigInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; +use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; use ContaoCommunityAlliance\DcGeneral\View\ViewTemplateInterface; /** @@ -36,21 +38,21 @@ class DefaultSearchElement extends AbstractElement implements SearchElementInter * * @var array */ - private $arrProperties; + private array $arrProperties = []; /** * The currently active property to be searched on. * * @var string */ - private $strSelectedProperty; + private string $strSelectedProperty = ''; /** * The current value to be searched. * * @var mixed */ - private $mixValue; + private mixed $mixValue = null; /** * Retrieve the persistent value from the input provider. @@ -59,13 +61,16 @@ class DefaultSearchElement extends AbstractElement implements SearchElementInter */ protected function getPersistent() { + $definition = $this->getEnvironment()->getDataDefinition(); + assert($definition instanceof ContainerInterface); + $values = []; if ($this->getSessionStorage()->has('search')) { $values = $this->getSessionStorage()->get('search'); } - if (\array_key_exists($this->getEnvironment()->getDataDefinition()->getName(), $values)) { - return $values[$this->getEnvironment()->getDataDefinition()->getName()]; + if (\array_key_exists($definition->getName(), $values)) { + return $values[$definition->getName()]; } return []; @@ -81,9 +86,12 @@ protected function getPersistent() */ protected function setPersistent($propertyName, $searchValue) { - $values = []; - $definitionName = $this->getEnvironment()->getDataDefinition()->getName(); + $definition = $this->getEnvironment()->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $definitionName = $definition->getName(); + $values = []; if ($this->getSessionStorage()->has('search')) { $values = $this->getSessionStorage()->get('search'); } @@ -93,12 +101,8 @@ protected function setPersistent($propertyName, $searchValue) $values[$definitionName] = []; } - if ($searchValue) { - $values[$definitionName]['field'] = $propertyName; - $values[$definitionName]['value'] = $searchValue; - } else { - unset($values[$definitionName]); - } + $values[$definitionName]['field'] = $propertyName; + $values[$definitionName]['value'] = $searchValue; } else { unset($values[$definitionName]); } @@ -109,14 +113,16 @@ protected function setPersistent($propertyName, $searchValue) /** * {@inheritdoc} */ - public function initialize(ConfigInterface $filterConfig, PanelElementInterface $objElement = null) + public function initialize(ConfigInterface $config, PanelElementInterface $element = null) { $session = $this->getSessionStorage(); $input = $this->getInputProvider(); - $value = null; - $field = null; + assert($input instanceof InputProviderInterface); + + $value = ''; + $field = ''; - if ('1' !== $this->getEnvironment()->getInputProvider()->getValue('filter_reset')) { + if ('1' !== $input->getValue('filter_reset')) { if ($input->hasValue('tl_field') && $this->getPanel()->getContainer()->updateValues()) { $field = $input->getValue('tl_field'); $value = $input->getValue('tl_value'); @@ -133,19 +139,19 @@ public function initialize(ConfigInterface $filterConfig, PanelElementInterface $this->setSelectedProperty($field); $this->setValue($value); } else { - $this->setPersistent(null, null); + $this->setPersistent('', ''); } if (!($this->getSelectedProperty() && $this->getValue())) { return; } - $currents = $filterConfig->getFilter(); + $currents = $config->getFilter(); if (!\is_array($currents)) { $currents = []; } - $filterConfig->setFilter( + $config->setFilter( \array_merge_recursive( $currents, [ @@ -164,20 +170,21 @@ public function initialize(ConfigInterface $filterConfig, PanelElementInterface */ public function render(ViewTemplateInterface $viewTemplate) { + $definition = $this->getEnvironment()->getDataDefinition(); + assert($definition instanceof ContainerInterface); + $options = []; foreach ($this->getPropertyNames() as $field) { - $lLabels = $this - ->getEnvironment() - ->getDataDefinition() + $lLabels = $definition ->getPropertiesDefinition() ->getProperty($field) ->getLabel(); $options[] = [ - 'value' => $field, - 'content' => \is_array($lLabels) ? $lLabels[0] : $lLabels, - 'attributes' => ($field === $this->getSelectedProperty()) ? ' selected' : '' - ]; + 'value' => $field, + 'content' => $lLabels, + 'attributes' => ($field === $this->getSelectedProperty()) ? ' selected' : '' + ]; } $viewTemplate->set('class', 'tl_select' . (!empty($this->getValue()) ? ' active' : '')); @@ -190,9 +197,9 @@ public function render(ViewTemplateInterface $viewTemplate) /** * {@inheritDoc} */ - public function addProperty($propertyName) + public function addProperty($strProperty) { - $this->arrProperties[] = $propertyName; + $this->arrProperties[] = $strProperty; return $this; } @@ -208,9 +215,9 @@ public function getPropertyNames(): array /** * {@inheritDoc} */ - public function setSelectedProperty($propertyName = '') + public function setSelectedProperty($strProperty = '') { - $this->strSelectedProperty = $propertyName; + $this->strSelectedProperty = $strProperty; return $this; } diff --git a/src/Panel/DefaultSortElement.php b/src/Panel/DefaultSortElement.php index 22346d94..3f5b83a5 100644 --- a/src/Panel/DefaultSortElement.php +++ b/src/Panel/DefaultSortElement.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2022 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -17,7 +17,7 @@ * @author Ingolf Steinhardt * @author Sven Baumann * @author Cliff Parnitzky - * @copyright 2013-2022 Contao Community Alliance. + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -27,10 +27,11 @@ use Contao\StringUtil; use ContaoCommunityAlliance\DcGeneral\Contao\DataDefinition\Definition\Contao2BackendViewDefinitionInterface; use ContaoCommunityAlliance\DcGeneral\Data\ConfigInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\GroupAndSortingDefinitionCollectionInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\GroupAndSortingDefinitionInterface; +use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; use ContaoCommunityAlliance\DcGeneral\View\ViewTemplateInterface; -use phpDocumentor\Reflection\Types\Mixed_; /** * Default implementation of a sort element. @@ -40,21 +41,22 @@ class DefaultSortElement extends AbstractElement implements SortElementInterface /** * The selected definition. * - * @var GroupAndSortingDefinitionInterface + * @var GroupAndSortingDefinitionInterface|null */ - private $selected; + private $selected = null; /** * Retrieve the group and sorting definition. * - * @return GroupAndSortingDefinitionCollectionInterface|GroupAndSortingDefinitionInterface[] + * @return GroupAndSortingDefinitionCollectionInterface */ protected function getGroupAndSortingDefinition() { + $definition = $this->getEnvironment()->getDataDefinition(); + assert($definition instanceof ContainerInterface); + /** @var Contao2BackendViewDefinitionInterface $view */ - $view = $this->getEnvironment() - ->getDataDefinition() - ->getDefinition(Contao2BackendViewDefinitionInterface::NAME); + $view = $definition->getDefinition(Contao2BackendViewDefinitionInterface::NAME); return $view ->getListingConfig() @@ -62,7 +64,7 @@ protected function getGroupAndSortingDefinition() } /** - * Search a definition by it's name. + * Search a definition by its name. * * @param string $name The name. * @@ -82,20 +84,23 @@ protected function searchDefinitionByName($name) /** * Retrieve the persistent value from the input provider. * - * @return array + * @return string */ protected function getPersistent() { + $definition = $this->getEnvironment()->getDataDefinition(); + assert($definition instanceof ContainerInterface); + $values = []; if ($this->getSessionStorage()->has('sorting')) { $values = $this->getSessionStorage()->get('sorting'); } - if (\array_key_exists($this->getEnvironment()->getDataDefinition()->getName(), $values)) { - return $values[$this->getEnvironment()->getDataDefinition()->getName()]; + if (\array_key_exists($definition->getName(), $values)) { + return $values[$definition->getName()]; } - return []; + return ''; } /** @@ -107,18 +112,18 @@ protected function getPersistent() */ protected function setPersistent($propertyName) { - $values = []; - $definitionName = $this->getEnvironment()->getDataDefinition()->getName(); + $definition = $this->getEnvironment()->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $definitionName = $definition->getName(); + + $values = []; if ($this->getSessionStorage()->has('sorting')) { $values = $this->getSessionStorage()->get('sorting'); } if ($propertyName) { - if (isset($values[$definitionName]) && !\is_array($values[$definitionName])) { - $values[$definitionName] = []; - } - $values[$definitionName] = $propertyName; } else { unset($values[$definitionName]); @@ -130,18 +135,14 @@ protected function setPersistent($propertyName) /** * {@inheritDoc} */ - public function initialize(ConfigInterface $config, PanelElementInterface $panelElement = null) + public function initialize(ConfigInterface $config, PanelElementInterface $element = null) { - $this->defineSortOption($panelElement); + $this->defineSortOption($element); $current = $config->getSorting(); - if (!\is_array($current)) { - $current = []; - } - - if ($this->getSelectedDefinition()) { - foreach ($this->getSelectedDefinition() as $information) { + if (null !== $selected = $this->getSelectedDefinition()) { + foreach ($selected as $information) { $current[$information->getProperty()] = $information->getSortingMode(); } } @@ -162,9 +163,11 @@ private function defineSortOption(?PanelElementInterface $panelElement): void } $input = $this->getInputProvider(); - $value = null; + assert($input instanceof InputProviderInterface); - if ('1' !== $this->getEnvironment()->getInputProvider()->getValue('filter_reset')) { + $value = ''; + + if ('1' !== $input->getValue('filter_reset')) { if ($input->hasValue('tl_sort') && $this->getPanel()->getContainer()->updateValues()) { $value = $input->getValue('tl_sort'); @@ -181,7 +184,7 @@ private function defineSortOption(?PanelElementInterface $panelElement): void $this->setSelected($persistent); } else { - $this->setPersistent(null); + $this->setPersistent(''); } } @@ -190,11 +193,14 @@ private function defineSortOption(?PanelElementInterface $panelElement): void */ public function render(ViewTemplateInterface $viewTemplate) { + $definition = $this->getEnvironment()->getDataDefinition(); + assert($definition instanceof ContainerInterface); + $options = []; foreach ($this->getGroupAndSortingDefinition() as $information) { /** @var GroupAndSortingDefinitionInterface $information */ $name = $information->getName(); - $properties = $this->getEnvironment()->getDataDefinition()->getPropertiesDefinition(); + $properties = $definition->getPropertiesDefinition(); if ($properties->hasProperty($name)) { $name = $properties->getProperty($name)->getLabel(); } diff --git a/src/Panel/DefaultSubmitElement.php b/src/Panel/DefaultSubmitElement.php index 7e6ffd5b..fdd16990 100644 --- a/src/Panel/DefaultSubmitElement.php +++ b/src/Panel/DefaultSubmitElement.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -32,7 +33,7 @@ class DefaultSubmitElement extends AbstractElement implements SubmitElementInter /** * {@inheritdoc} */ - public function initialize(ConfigInterface $config, PanelElementInterface $panelElement = null) + public function initialize(ConfigInterface $config, PanelElementInterface $element = null) { } @@ -41,5 +42,6 @@ public function initialize(ConfigInterface $config, PanelElementInterface $panel */ public function render(ViewTemplateInterface $viewTemplate) { + return $this; } } diff --git a/src/Panel/PanelContainerInterface.php b/src/Panel/PanelContainerInterface.php index d59602a5..b38309fd 100644 --- a/src/Panel/PanelContainerInterface.php +++ b/src/Panel/PanelContainerInterface.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -28,6 +29,8 @@ * This interface describes a panel container. * * A Panel container contains panels which contain panel elements. + * + * @extends \IteratorAggregate */ interface PanelContainerInterface extends \IteratorAggregate, \Countable { @@ -50,31 +53,31 @@ public function setEnvironment(EnvironmentInterface $objEnvironment); /** * Add a panel to the container. * - * @param string $strKey Name of the panel. - * @param PanelInterface $objPanel The panel to add. + * @param string $panelName Name of the panel. + * @param PanelInterface $panel The panel to add. * * @return PanelContainerInterface */ - public function addPanel($strKey, $objPanel); + public function addPanel($panelName, $panel); /** * Retrieve a panel from the container. * - * @param string $strKey The name of the panel. + * @param string $panelName The name of the panel. * * @return PanelInterface */ - public function getPanel($strKey); + public function getPanel($panelName); /** * Initialize all panels and apply all restrictions to the given Config. * - * @param ConfigInterface $objConfig The data config to be populated with the element values. - * @param PanelElementInterface $objElement The element currently being initialized. + * @param ConfigInterface $config The data config to be populated with the element values. + * @param PanelElementInterface|null $element The element currently being initialized. * * @return PanelContainerInterface */ - public function initialize(ConfigInterface $objConfig, PanelElementInterface $objElement = null); + public function initialize(ConfigInterface $config, PanelElementInterface $element = null); /** * Determinator if the panels should be updated from the InputProvider or not. diff --git a/src/Panel/PanelElementInterface.php b/src/Panel/PanelElementInterface.php index 3cef7230..825d28bd 100644 --- a/src/Panel/PanelElementInterface.php +++ b/src/Panel/PanelElementInterface.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -39,28 +40,28 @@ public function getPanel(); /** * Return the parenting panel. * - * @param PanelInterface $objPanel The panel to use as parent. + * @param PanelInterface $panelElement The panel to use as parent. * * @return PanelElementInterface */ - public function setPanel(PanelInterface $objPanel); + public function setPanel(PanelInterface $panelElement); /** * Initialize the passed configuration with the values of the element. * - * @param ConfigInterface $objConfig The config to which the initialization shall be applied to. - * @param PanelElementInterface $objElement The element to be initialized (if any). + * @param ConfigInterface $config The config to which the initialization shall be applied to. + * @param PanelElementInterface|null $element The element to be initialized (if any). * * @return void */ - public function initialize(ConfigInterface $objConfig, PanelElementInterface $objElement = null); + public function initialize(ConfigInterface $config, PanelElementInterface $element = null); /** * Render the element using the given Template. * - * @param ViewTemplateInterface $objTemplate The Template to use. + * @param ViewTemplateInterface $viewTemplate The Template to use. * - * @return PanelElementInterface + * @return self */ - public function render(ViewTemplateInterface $objTemplate); + public function render(ViewTemplateInterface $viewTemplate); } diff --git a/src/Panel/PanelInterface.php b/src/Panel/PanelInterface.php index 4d853105..ee825928 100644 --- a/src/Panel/PanelInterface.php +++ b/src/Panel/PanelInterface.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -27,6 +28,8 @@ * This interface describes a panel. * * A panel is a row of a panel container. + * + * @extends \IteratorAggregate */ interface PanelInterface extends \IteratorAggregate, \Countable { @@ -40,38 +43,38 @@ public function getContainer(); /** * Set the parenting container. * - * @param PanelContainerInterface $objContainer The Container to be used as parent. + * @param PanelContainerInterface $container The Container to be used as parent. * * @return PanelInterface */ - public function setContainer(PanelContainerInterface $objContainer); + public function setContainer(PanelContainerInterface $container); /** * Add an element to the panel. * - * @param string $strKey Name of the panel. - * @param PanelElementInterface $objElement The element instance to add. + * @param string $panelName Name of the panel. + * @param PanelElementInterface $element The element instance to add. * * @return mixed */ - public function addElement($strKey, $objElement); + public function addElement($panelName, $element); /** * Retrieve an element with the given name. * - * @param string $strKey The name of the element. + * @param string $elementName The name of the element. * - * @return PanelElementInterface + * @return PanelElementInterface|null */ - public function getElement($strKey); + public function getElement($elementName); /** * Initialize the passed config via all contained elements. * - * @param ConfigInterface $objConfig The config to which the initialization shall be applied to. - * @param PanelElementInterface $objElement The element to be initialized (if any). + * @param ConfigInterface $config The config to which the initialization shall be applied to. + * @param PanelElementInterface|null $element The element to be initialized (if any). * * @return void */ - public function initialize(ConfigInterface $objConfig, PanelElementInterface $objElement = null); + public function initialize(ConfigInterface $config, PanelElementInterface $element = null); } diff --git a/src/Panel/SortElementInterface.php b/src/Panel/SortElementInterface.php index bf052a92..21cbe383 100644 --- a/src/Panel/SortElementInterface.php +++ b/src/Panel/SortElementInterface.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -22,7 +23,6 @@ namespace ContaoCommunityAlliance\DcGeneral\Panel; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\GroupAndSortingDefinitionInterface; -use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\GroupAndSortingInformationInterface; /** * This interface describes a sort panel element. @@ -34,21 +34,21 @@ interface SortElementInterface extends PanelElementInterface * * @param string $name The name of the definition to mark as selected. * - * @return SearchElementInterface + * @return self */ public function setSelected($name); /** * Return the name of the currently selected definition. * - * @return string + * @return string|null */ public function getSelected(); /** * Return the currently selected definition. * - * @return GroupAndSortingDefinitionInterface|GroupAndSortingInformationInterface[] + * @return GroupAndSortingDefinitionInterface|null */ public function getSelectedDefinition(); } diff --git a/src/Resources/config/contao/picker_provider.yml b/src/Resources/config/contao/picker_provider.yml index 5a7765fe..03964f72 100644 --- a/src/Resources/config/contao/picker_provider.yml +++ b/src/Resources/config/contao/picker_provider.yml @@ -5,10 +5,12 @@ services: arguments: - "@knp_menu.factory" - "@router" + - "@?translator" + - "@security.helper" calls: - [setTokenStorage, ["@security.token_storage"]] tags: - - name: contao.picker_provider + - name: contao.picker_provider cca.picker.tree_picker_provider: class: ContaoCommunityAlliance\DcGeneral\Contao\Picker\TreePickerProvider @@ -20,4 +22,4 @@ services: calls: - [setTokenStorage, ["@security.token_storage"]] tags: - - name: contao.picker_provider + - name: contao.picker_provider diff --git a/src/Resources/contao/templates/dcbe_general_edit.html5 b/src/Resources/contao/templates/dcbe_general_edit.html5 index 12f120c4..5665d940 100644 --- a/src/Resources/contao/templates/dcbe_general_edit.html5 +++ b/src/Resources/contao/templates/dcbe_general_edit.html5 @@ -74,7 +74,7 @@ $this->insert( -fieldsets as $arrFieldset): if($arrFieldset['legend']): ?> +fieldsets as $arrFieldset): if($arrFieldset['legend'] ?? null): ?>
diff --git a/src/Resources/contao/templates/dcbe_general_show.html5 b/src/Resources/contao/templates/dcbe_general_show.html5 index d4f570ed..c257f421 100644 --- a/src/Resources/contao/templates/dcbe_general_show.html5 +++ b/src/Resources/contao/templates/dcbe_general_show.html5 @@ -5,7 +5,7 @@ 'languages' => $this->languages, 'request' => $this->Environment->request, 'submit' => $this->languageSubmit, - 'REQUEST_TOKEN' => REQUEST_TOKEN, + 'REQUEST_TOKEN' => $this->REQUEST_TOKEN, ]) ?>
diff --git a/src/View/ActionHandler/AbstractEnvironmentAwareHandler.php b/src/View/ActionHandler/AbstractEnvironmentAwareHandler.php index 8ac25cf8..c33b9801 100644 --- a/src/View/ActionHandler/AbstractEnvironmentAwareHandler.php +++ b/src/View/ActionHandler/AbstractEnvironmentAwareHandler.php @@ -26,11 +26,13 @@ use ContaoCommunityAlliance\DcGeneral\Event\ActionEvent; /** - * The AbstractEnvironmentAwareHandler is designed for action handlers which can also be used in a non event context. + * The AbstractEnvironmentAwareHandler is designed for action handlers which can also be used in a nonevent context. * * It provides a setEnvironment method which has to be used to initialize the environment instead. * * @deprecated This class is deprecated as it is an event listener with a changing state and will get removed. + * + * @psalm-suppress DeprecatedClass */ abstract class AbstractEnvironmentAwareHandler extends AbstractHandler implements EnvironmentAwareInterface { diff --git a/src/View/ActionHandler/AbstractPropertyOverrideEditAllHandler.php b/src/View/ActionHandler/AbstractPropertyOverrideEditAllHandler.php index 7b2a6051..5f2a5978 100644 --- a/src/View/ActionHandler/AbstractPropertyOverrideEditAllHandler.php +++ b/src/View/ActionHandler/AbstractPropertyOverrideEditAllHandler.php @@ -14,7 +14,7 @@ * @author Sven Baumann * @author Richard Henkenjohann * @author Ingolf Steinhardt - * @copyright 2013-2023s Contao Community Alliance. + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0 * @filesource */ @@ -31,6 +31,8 @@ use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\ContaoBackendViewTemplate; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\GetBreadcrumbEvent; use ContaoCommunityAlliance\DcGeneral\Data\CollectionInterface; +use ContaoCommunityAlliance\DcGeneral\Data\DataProviderInterface; +use ContaoCommunityAlliance\DcGeneral\Data\EditInformationInterface; use ContaoCommunityAlliance\DcGeneral\Data\ModelId; use ContaoCommunityAlliance\DcGeneral\Data\ModelIdInterface; use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; @@ -43,7 +45,9 @@ use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralRuntimeException; use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; use ContaoCommunityAlliance\DcGeneral\SessionStorageInterface; +use ContaoCommunityAlliance\Translator\TranslatorInterface; use LogicException; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; /** * This class is the abstract base for override/edit all "overrideAll/editAll" commands. @@ -53,6 +57,8 @@ */ abstract class AbstractPropertyOverrideEditAllHandler extends AbstractPropertyVisibilityHandler { + use CallActionTrait; + /** * Handle submit triggered button. * @@ -69,6 +75,7 @@ protected function handleSubmit(Action $action, EnvironmentInterface $environmen $inputProvider = $this->getInputProvider($environment); $sessionStorage = $this->getSessionStorage($environment); $eventDispatcher = $environment->getEventDispatcher(); + assert($eventDispatcher instanceof EventDispatcherInterface); if ( ('auto' === $inputProvider->getValue('SUBMIT_TYPE')) @@ -110,6 +117,7 @@ protected function editCollection( ) { while ($collection->count() > 0) { $model = $collection->shift(); + assert($model instanceof ModelInterface); $persistPropertyValueBag = $this->cloneCleanPropertyValueBag($action, $propertyValueBag, $model, $environment); @@ -131,6 +139,7 @@ protected function editCollection( protected function updateErrorInformation( \ArrayObject $renderInformation ) { + /** @var array>>|null $modelError */ $modelError = $renderInformation->offsetExists('modelError') ? $renderInformation->offsetGet('modelError') : null; @@ -232,8 +241,10 @@ private function markPropertyInvalidErrorsByModel( EnvironmentInterface $environment ) { $editInformation = System::getContainer()->get('cca.dc-general.edit-information'); + assert($editInformation instanceof EditInformationInterface); + $errorInformation = $editInformation->getModelError($model); - if (null === $errorInformation) { + if (!$errorInformation) { return; } @@ -301,7 +312,9 @@ private function updatePropertyValueBag( PropertyValueBagInterface $updateBag, EnvironmentInterface $environment ) { - $dataProvider = $environment->getDataProvider(); + $dataProvider = $environment->getDataProvider(); + assert($dataProvider instanceof DataProviderInterface); + $sessionProperties = $this->getPropertiesFromSession($action, $environment); foreach (\array_keys($sessionProperties) as $sessionPropertyName) { @@ -311,6 +324,8 @@ private function updatePropertyValueBag( if (!$updateBag->isPropertyValueInvalid($sessionPropertyName)) { $editModel = $dataProvider->fetch($dataProvider->getEmptyConfig()->setId($model->getId())); + assert($editModel instanceof ModelInterface); + $updateBag->setPropertyValue( $sessionPropertyName, $editModel->getProperty($sessionPropertyName) @@ -348,8 +363,11 @@ private function handleEditHandler( PropertyValueBagInterface $propertyValueBag, EnvironmentInterface $environment ) { - $inputProvider = $this->getInputProvider($environment); + $inputProvider = $this->getInputProvider($environment); + assert($inputProvider instanceof InputProviderInterface); + $editInformation = System::getContainer()->get('cca.dc-general.edit-information'); + assert($editInformation instanceof EditInformationInterface); $inputValues = $this->handleInputValues($action, $model, $environment); @@ -370,7 +388,7 @@ private function handleEditHandler( $this->restoreInputValues($action, $model, $propertyValueBag, $inputValues, $environment); $errorInformation = $editInformation->getModelError($model); - if (null !== $errorInformation) { + if ($errorInformation) { foreach (\array_keys($errorInformation) as $errorPropertyName) { if (false === $propertyValueBag->hasPropertyValue($errorPropertyName)) { continue; @@ -407,9 +425,11 @@ private function handleInputValues(Action $action, ModelInterface $model, Enviro } $inputProvider = $this->getInputProvider($environment); + assert($inputProvider instanceof InputProviderInterface); $inputValues = []; foreach (\array_keys($_POST) as $valueName) { + $valueName = (string) $valueName; $inputValues[$valueName] = $inputProvider->getValue($valueName, true); $inputProvider->unsetValue($valueName); @@ -479,11 +499,12 @@ protected function restoreInputValues( * @param Action $action The action. * @param EnvironmentInterface $environment The environment. * - * @return array + * @return string */ protected function getEditButtons(Action $action, EnvironmentInterface $environment) { $translator = $environment->getTranslator(); + assert($translator instanceof TranslatorInterface); $mode = $this->getMode($action); @@ -548,7 +569,9 @@ protected function getPropertyValueBagFromModel( EnvironmentInterface $environment ) { $propertiesDefinition = $this->getDataDefinition($environment)->getPropertiesDefinition(); - $editInformation = System::getContainer()->get('cca.dc-general.edit-information'); + + $editInformation = System::getContainer()->get('cca.dc-general.edit-information'); + assert($editInformation instanceof EditInformationInterface); $propertyValueBag = new PropertyValueBag(); @@ -594,6 +617,7 @@ protected function getCollectionFromSession(Action $action, EnvironmentInterface $sessionStorage = $this->getSessionStorage($environment); $dataDefinition = $this->getDataDefinition($environment); $dataProvider = $environment->getDataProvider($dataDefinition->getName()); + assert($dataProvider instanceof DataProviderInterface); $addEditProperties = $inputProvider->hasValue('FORM_INPUTS') @@ -608,8 +632,9 @@ protected function getCollectionFromSession(Action $action, EnvironmentInterface $modelIds[] = ModelId::fromSerialized($modelId)->getId(); if ($addEditProperties) { - $modelEditProperties = $inputProvider->getValue(\str_replace('::', '____', $modelId) . '_', true); - $inputProvider->unsetValue(\str_replace('::', '____', $modelId) . '_'); + $transformed = \str_replace('::', '____', (string) $modelId) . '_'; + $modelEditProperties = $inputProvider->getValue($transformed, true); + $inputProvider->unsetValue($transformed); $editProperties[$modelId] = $modelEditProperties; } @@ -621,6 +646,7 @@ protected function getCollectionFromSession(Action $action, EnvironmentInterface [['operation' => 'IN', 'property' => $idProperty, 'values' => $modelIds]] ) ); + assert($collection instanceof CollectionInterface); $session['editProperties'] = $editProperties; @@ -644,7 +670,7 @@ protected function getEditPropertiesByModelId( EnvironmentInterface $environment ) { $session = $this->getSession($action, $environment); -//dd($session['editProperties']); + return $session['editProperties'][$modelId->getSerialized()] ?? []; } @@ -660,8 +686,11 @@ protected function getEditPropertiesByModelId( */ protected function renderBreadcrumb(EnvironmentInterface $environment) { + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + $event = new GetBreadcrumbEvent($environment); - $environment->getEventDispatcher()->dispatch($event, $event::NAME); + $dispatcher->dispatch($event, $event::NAME); $elements = $event->getElements(); if (empty($elements)) { return null; @@ -692,15 +721,20 @@ protected function revertValuesByErrors( EnvironmentInterface $environment ) { $editInformation = System::getContainer()->get('cca.dc-general.edit-information'); + assert($editInformation instanceof EditInformationInterface); + if (!$editInformation->hasAnyModelError()) { return; } $dataProvider = $environment->getDataProvider(); + assert($dataProvider instanceof DataProviderInterface); + $properties = $this->getPropertiesFromSession($action, $environment); while ($collection->count() > 0) { $model = $collection->shift(); + assert($model instanceof ModelInterface); $modelErrors = $editInformation->getModelError($model); if (!$modelErrors && ('edit' === $this->getMode($action))) { @@ -708,6 +742,7 @@ protected function revertValuesByErrors( } $revertModel = $dataProvider->fetch($dataProvider->getEmptyConfig()->setId($model->getId())); + assert($revertModel instanceof ModelInterface); $originalModel = clone $revertModel; $revertModel->setId($revertModel->getId()); @@ -741,7 +776,11 @@ private function handlePostPersist( EnvironmentInterface $environment ) { $event = new PostPersistModelEvent($environment, $model, $originalModel); - $environment->getEventDispatcher()->dispatch($event, $event::NAME); + + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $dispatcher->dispatch($event, $event::NAME); } /** @@ -765,6 +804,7 @@ protected function getSession(Action $action, EnvironmentInterface $environment) { $dataDefinition = $this->getDataDefinition($environment); $sessionStorage = $environment->getSessionStorage(); + assert($sessionStorage instanceof SessionStorageInterface); $session = $sessionStorage->get($dataDefinition->getName() . '.' . $this->getMode($action)); diff --git a/src/View/ActionHandler/AbstractPropertyVisibilityHandler.php b/src/View/ActionHandler/AbstractPropertyVisibilityHandler.php index 66713b4b..71fdd757 100644 --- a/src/View/ActionHandler/AbstractPropertyVisibilityHandler.php +++ b/src/View/ActionHandler/AbstractPropertyVisibilityHandler.php @@ -30,6 +30,7 @@ use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\PalettesDefinitionInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\Properties\PropertyInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Condition\Property\PropertyConditionInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Condition\Property\PropertyTrueCondition; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\PaletteInterface; use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; @@ -389,7 +390,10 @@ private function invisiblePaletteProperty( continue; } - if (false === $searchProperty->getVisibleCondition()->match($intersectModel)) { + $visibleCondition = $searchProperty->getVisibleCondition(); + assert($visibleCondition instanceof PropertyConditionInterface); + + if (false === $visibleCondition->match($intersectModel)) { continue; } diff --git a/src/View/ViewTemplateInterface.php b/src/View/ViewTemplateInterface.php index 9941d11c..7f691598 100644 --- a/src/View/ViewTemplateInterface.php +++ b/src/View/ViewTemplateInterface.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Tristan Lins * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -29,11 +30,11 @@ interface ViewTemplateInterface /** * Set the template data from an array. * - * @param array $data The data array. + * @param array $arrData The data array. * - * @return ViewTemplateInterface + * @return self */ - public function setData($data); + public function setData($arrData); /** * Return the template data as array. @@ -48,7 +49,7 @@ public function getData(); * @param string $name Name of the value. * @param mixed $value The value to add to the template. * - * @return ViewTemplateInterface + * @return self */ public function set($name, $value); diff --git a/tests/Data/DefaultDataProviderTest.php b/tests/Data/DefaultDataProviderTest.php index 92dc82e2..80e6e966 100644 --- a/tests/Data/DefaultDataProviderTest.php +++ b/tests/Data/DefaultDataProviderTest.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2023 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,8 @@ * @package contao-community-alliance/dc-general * @author Christian Schiffler * @author Sven Baumann - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2023 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -52,7 +53,7 @@ private function mockDatabase() return $this ->getMockBuilder(Database::class) ->disableOriginalConstructor() - ->setMethods(['__destruct', 'listFields']) + ->onlyMethods(['__destruct', 'listFields']) ->getMockForAbstractClass(); } @@ -61,7 +62,7 @@ private function mockConnection() return $this ->getMockBuilder(Connection::class) ->disableOriginalConstructor() - ->setMethods(['getSchemaManager']) + ->onlyMethods(['createSchemaManager']) ->getMock(); } @@ -75,19 +76,19 @@ private function mockDefaultProvider() $schemaTable = $this ->getMockBuilder(Table::class) ->disableOriginalConstructor() - ->setMethods(['hasColumn']) + ->onlyMethods(['hasColumn']) ->getMock(); $schemaTable->method('hasColumn')->willReturn(false); $schemaManager = $this ->getMockBuilder(AbstractSchemaManager::class) ->disableOriginalConstructor() - ->setMethods(['listTableDetails']) + ->onlyMethods(['introspectTable']) ->getMockForAbstractClass(); - $schemaManager->method('listTableDetails')->willReturn($schemaTable); + $schemaManager->method('introspectTable')->willReturn($schemaTable); $connection = $this->mockConnection(); - $connection->method('getSchemaManager')->willReturn($schemaManager); + $connection->method('createSchemaManager')->willReturn($schemaManager); //$database->method('listFields')->willReturn([]); $dataProvider = new DefaultDataProvider(); @@ -122,19 +123,19 @@ public function testSetBaseConfigDeprecatedDatabase() $schemaTable = $this ->getMockBuilder(Table::class) ->disableOriginalConstructor() - ->setMethods(['hasColumn']) + ->onlyMethods(['hasColumn']) ->getMock(); $schemaTable->method('hasColumn')->willReturn(false); $schemaManager = $this ->getMockBuilder(AbstractSchemaManager::class) ->disableOriginalConstructor() - ->setMethods(['listTableDetails']) + ->onlyMethods(['introspectTable']) ->getMockForAbstractClass(); - $schemaManager->method('listTableDetails')->willReturn($schemaTable); + $schemaManager->method('introspectTable')->willReturn($schemaTable); $connection = $this->mockConnection(); - $connection->method('getSchemaManager')->willReturn($schemaManager); + $connection->method('createSchemaManager')->willReturn($schemaManager); $reflection = new \ReflectionProperty(Database::class, 'resConnection'); $reflection->setAccessible(true); @@ -160,7 +161,7 @@ public function testSetBaseConfigDeprecatedDatabase() $reflection->setAccessible(true); self::assertSame('id', $reflection->getValue($dataProvider)); - self::assertFalse($dataProvider->getTimeStampProperty()); + self::assertNull($dataProvider->getTimeStampProperty()); self::assertNull($dataProvider->getIdGenerator()); } @@ -186,24 +187,24 @@ public function testSetBaseConfigForGetDefaultConnection() $schemaTable = $this ->getMockBuilder(Table::class) ->disableOriginalConstructor() - ->setMethods(['hasColumn']) + ->onlyMethods(['hasColumn']) ->getMock(); $schemaTable->method('hasColumn')->willReturn(false); $schemaManager = $this ->getMockBuilder(AbstractSchemaManager::class) ->disableOriginalConstructor() - ->setMethods(['listTableDetails']) + ->onlyMethods(['introspectTable']) ->getMockForAbstractClass(); - $schemaManager->method('listTableDetails')->willReturn($schemaTable); + $schemaManager->method('introspectTable')->willReturn($schemaTable); $connection = $this->mockConnection(); - $connection->method('getSchemaManager')->willReturn($schemaManager); + $connection->method('createSchemaManager')->willReturn($schemaManager); $dataProvider = $this ->getMockBuilder(DefaultDataProvider::class) ->disableOriginalConstructor() - ->setMethods(['getDefaultConnection']) + ->onlyMethods(['getDefaultConnection']) ->getMock(); $dataProvider->method('getDefaultConnection')->willReturn($connection);