diff --git a/.composer-require-checker.json b/.composer-require-checker.json index ae3559522..9f8d39013 100644 --- a/.composer-require-checker.json +++ b/.composer-require-checker.json @@ -18,6 +18,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 f6c755d93..bddb943bd 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/composer.json b/composer.json index 01b6afafd..d9de59be9 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 1e76d964b..45470c93b 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 9a8a9accd..a3115ad09 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 c4e478ba9..59db1cc6e 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/Dca/Populator/BackendViewPopulator.php b/src/Contao/Dca/Populator/BackendViewPopulator.php index 01a39f4e6..977a29f85 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 8ce72df1c..f7d4ff57f 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 0247535a0..bec33a78e 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 52bbfe420..b5487de02 100644 --- a/src/Contao/Picker/PagePickerProvider.php +++ b/src/Contao/Picker/PagePickerProvider.php @@ -22,12 +22,42 @@ 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 +71,7 @@ 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 d5354c705..b6d7956a2 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 017a3ab46..0667b7d01 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 da4222c5b..beacd3352 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() { @@ -94,18 +97,27 @@ public function handlePostDuplicateModelEvent(PostDuplicateModelEvent $event) */ 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 +126,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 +140,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); } @@ -147,8 +166,15 @@ 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 +182,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 449bf3a18..c08a06ee4 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 5821695dd..2450af043 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,22 @@ private function generateHeaderButtons(EnvironmentInterface $environment) * * @return void */ - private function renderCollection(EnvironmentInterface $environment, CollectionInterface $collection, $grouping) + private function renderCollection(EnvironmentInterface $environment, CollectionInterface $collection, array $grouping) { - $listing = $this->getViewSection($environment->getDataDefinition())->getListingConfig(); + $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 +503,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 +514,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 +583,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 +598,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()) @@ -572,13 +644,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 +672,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 +690,51 @@ 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 environment. + * @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 +755,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 +776,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 +828,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 +870,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 6cec1b9da..4ff8475e3 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 f5c07f22c..fcbef4e87 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 f2e74dee4..1e9c56983 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 7751ae739..4d9f61a05 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 147c86086..8f1286e92 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 995c09c3e..bad69dbf4 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 c438ae8de..fe87034f4 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,17 +24,24 @@ 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. @@ -56,7 +64,7 @@ public function __construct(RequestScopeDeterminator $scopeDeterminator) /** * {@inheritDoc} */ - public function handleEvent(ActionEvent $event) + public function handleEvent(ActionEvent $event): void { if ( !$this->getScopeDeterminator()->currentScopeIsBackend() @@ -65,7 +73,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 +91,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 +122,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 +132,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 +168,7 @@ protected function handleInvalidPropertyValueBag( } $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); foreach (\array_keys($propertyValueBag->getArrayCopy()) as $propertyName) { $allErrors = $propertyValueBag->getPropertyValueErrors($propertyName); @@ -172,7 +189,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 +260,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 +281,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); @@ -279,7 +300,7 @@ private function renderFieldSets( 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 +320,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 +344,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 +365,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 +396,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 +413,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 3b34d451d..9af7e0b7b 100644 --- a/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/PasteAllHandler.php +++ b/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/PasteAllHandler.php @@ -20,19 +20,26 @@ 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. @@ -47,16 +54,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 +79,7 @@ public function __construct(RequestScopeDeterminator $scopeDeterminator) /** * {@inheritDoc} */ - public function handleEvent(ActionEvent $event) + public function handleEvent(ActionEvent $event): void { if ( !$this->getScopeDeterminator()->currentScopeIsBackend() @@ -101,7 +108,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 +141,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 +177,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 +198,7 @@ protected function getCollection(EnvironmentInterface $environment) protected function getFlatCollection(EnvironmentInterface $environment) { $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); $previousItem = null; $collection = []; @@ -207,9 +233,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 +273,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 +339,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 +374,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 +401,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 +424,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 +446,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 56b22af9b..92d5a52dd 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 acbdfcc6f..0dbbd43cb 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); @@ -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 6cfd40914..5a7f00b77 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 a94d2c237..62141f14c 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 27de31fa5..796357791 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 7f6f9ee91..cdfb38229 100644 --- a/src/Contao/View/Contao2BackendView/ActionHandler/ShowHandler.php +++ b/src/Contao/View/Contao2BackendView/ActionHandler/ShowHandler.php @@ -31,17 +31,24 @@ 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\Data\DataProviderInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\Properties\PropertyInterface; use ContaoCommunityAlliance\DcGeneral\Data\ModelId; use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; use ContaoCommunityAlliance\DcGeneral\Data\MultiLanguageDataProviderInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\PropertiesDefinitionInterface; 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; use function sprintf; @@ -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 1dda36f14..6d497bfd7 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 332d4bb01..0ae5b309d 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; } @@ -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'); } /** @@ -298,15 +352,21 @@ protected function isSelectModeActive() 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 de0de2057..fdccf67d5 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,14 @@ 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; +use function Symfony\Component\String\s; /** - * 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 +68,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 +121,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 +183,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 +223,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 +257,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 +282,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 +297,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 +338,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 +360,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 +381,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 +437,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 +493,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 +528,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,17 +548,24 @@ 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()); - if ($parentProviderName = $basicDefinition->getParentDataProvider()) { + + $dataProvider = $basicDefinition->getDataProvider(); + assert(\is_string($dataProvider)); + + $filter->andModelIsFromProvider($dataProvider); + if ($parentProviderName = $dataProvider) { $filter->andParentIsFromProvider($parentProviderName); } else { $filter->andHasNoParent(); @@ -539,7 +583,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 +611,7 @@ private function renderImageAsHtml($src, $alt, $attributes = '') ContaoEvents::IMAGE_GET_HTML ); - return $imageEvent->getHtml(); + return $imageEvent->getHtml() ?? ''; } /** @@ -592,8 +639,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 +653,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 +696,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 6f74ad22d..5bc81bdca 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()); @@ -163,6 +164,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 3f9c39cea..faf68d225 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,8 +259,8 @@ 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 * @@ -248,10 +270,14 @@ protected function getUniqueId($propertyName) * @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,7 +474,7 @@ 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); $widget->validate(); @@ -471,7 +504,7 @@ public function processInput(PropertyValueBag $propertyValues) /** * {@inheritDoc} */ - public function processErrors(PropertyValueBag $propertyValues) + public function processErrors(PropertyValueBag $propertyValues): void { $propertyErrors = $propertyValues->getInvalidPropertyErrors(); @@ -480,6 +513,7 @@ public function processErrors(PropertyValueBag $propertyValues) } $dispatcher = $this->getEnvironment()->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); foreach ($propertyErrors as $property => $errors) { $widget = $this->getWidget($property); @@ -499,6 +533,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 f93d1ddd8..b393ee960 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 2a4704f8e..fe49489c2 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,9 +34,13 @@ 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; @@ -53,6 +58,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. @@ -109,20 +118,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 +140,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 +160,7 @@ public function __construct( ); // @codingStandardsIgnoreEnd $editInformation = System::getContainer()->get('cca.dc-general.edit-information'); + assert($editInformation instanceof EditInformationInterface); } $this->editInformation = $editInformation; @@ -170,7 +183,10 @@ public function getEnvironment() */ protected function getDataDefinition() { - return $this->getEnvironment()->getDataDefinition(); + $definition = $this->getEnvironment()->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + return $definition; } /** @@ -180,7 +196,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 +219,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 +248,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 +292,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 +323,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 +345,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 +372,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 +396,9 @@ protected function getButtonLabel($buttonLabel) */ protected function getEditButtons() { + $inputProvider = $this->getEnvironment()->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + $buttons = []; $basicDefinition = $this->getDataDefinition()->getBasicDefinition(); @@ -377,7 +418,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 +436,7 @@ protected function getEditButtons() if ( $basicDefinition->isCreatable() - && !$this->getEnvironment()->getInputProvider()->getParameter('nc') + && !$inputProvider->getParameter('nc') ) { $buttonTemplate->setData( [ @@ -412,7 +453,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 +493,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(); @@ -485,13 +529,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 +553,7 @@ protected function buildFieldSet($widgetManager, $palette, $propertyValues) $definition->getName() ); $fields = []; + $fieldSet = []; $properties = $legend->getProperties($this->model, $propertyValues); if (!$properties) { @@ -515,7 +568,6 @@ protected function buildFieldSet($widgetManager, $palette, $propertyValues) // If this property is invalid, fetch the error. if ( (!$isAutoSubmit) - && $propertyValues && $propertyValues->hasPropertyValue($property->getName()) && $propertyValues->isPropertyValueInvalid($property->getName()) ) { @@ -534,7 +586,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 +623,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 +639,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 +678,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 +709,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 +723,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 +739,7 @@ protected function getHeadline(): string /** * Determine the headline to use. * - * @return string. + * @return string */ protected function getSubHeadline(): string { @@ -680,9 +747,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); - return $event->getHeadline(); + $dispatcher->dispatch($event, $event::NAME); + + return (string) $event->getHeadline(); } /** @@ -695,9 +765,13 @@ protected function getSubHeadline(): string */ 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 +788,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 +857,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) { @@ -815,74 +906,98 @@ protected function allValuesUnique() */ 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); - $widgetManager = new ContaoWidgetManager($environment, $this->model); + $palettesDefinition = $definition->getPalettesDefinition(); + $submitted = ($definition->getName() === $inputProvider->getValue('FORM_SUBMIT')); + $isAutoSubmit = ('auto' === $inputProvider->getValue('SUBMIT_TYPE')); - $this->checkEditable($this->model); - $this->checkCreatable($this->model); + $editInformation = System::getContainer()->get('cca.dc-general.edit-information'); + assert($editInformation instanceof EditInformationInterface); + + $widgetManager = new ContaoWidgetManager($environment, $model); + + $this->checkEditable($model); + $this->checkCreatable($model); - $environment->getEventDispatcher()->dispatch( - new PreEditModelEvent($environment, $this->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) { + assert($propertyValues instanceof PropertyValueBag); + + if ($submitted) { // 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 +1015,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 +1064,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 +1095,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 b96695eab..50962ccfc 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 42f54c8b1..7c4d7cca4 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 d28e7bc8e..d76d9e636 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 */ @@ -50,6 +51,8 @@ class BuildWidgetEvent extends AbstractModelAwareEvent * The instantiated widget. * * @var Widget + * + * @psalm-suppress PropertyNotSetInConstructor */ protected $widget; diff --git a/src/Contao/View/Contao2BackendView/Event/DecodePropertyValueForWidgetEvent.php b/src/Contao/View/Contao2BackendView/Event/DecodePropertyValueForWidgetEvent.php index 861d1ce14..4e579940d 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 217f6d47c..e320471b5 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 794aff5b5..7b06d1886 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 cb5e8a914..7a7c80156 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/GetOperationButtonEvent.php b/src/Contao/View/Contao2BackendView/Event/GetOperationButtonEvent.php index a356f19e8..59e5d6abf 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 24a68a782..defe94ef7 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 c0e7a3c4c..dd9858e57 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 dc2b93b30..fefdde37f 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 c8c110dca..e860b0271 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 8fb46f4ad..6e746e7dd 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 f55148f35..9fe5e74c3 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 81c1fe374..1faba1d29 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 7237f44a2..d075341a8 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 bba77a071..fd5d40e09 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 955ebabb5..6665c7d1c 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 96031e3de..f143c04ca 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 72d677b32..b3bd27c9d 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 7e05d27c2..393a7d11f 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,12 +26,18 @@ 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; /** @@ -83,7 +89,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 +102,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 +153,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 +178,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 +212,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 11f6e40d6..129169d63 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 b391f4e17..60e45f317 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 31ecae19f..21c8246ac 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 ea164a21d..d8e03384a 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 57588b044..89011350a 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 3528fac10..78724e51c 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(), @@ -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); } /** @@ -293,6 +299,6 @@ private function formatByYearGrouping($value) */ private function getTimestamp($value) { - return ($value instanceof \DateTime) ? $value->getTimestamp() : $value; + return ($value instanceof \DateTime) ? $value->getTimestamp() : (int) $value; } } diff --git a/src/Contao/View/Contao2BackendView/Subscriber/MultipleHandlerSubscriber.php b/src/Contao/View/Contao2BackendView/Subscriber/MultipleHandlerSubscriber.php index 8e0164a47..de3bb61f3 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,7 +12,8 @@ * * @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 */ @@ -25,9 +26,13 @@ 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\InputProviderInterface; +use ContaoCommunityAlliance\DcGeneral\SessionStorageInterface; use MenAtWork\MultiColumnWizardBundle\Event\GetOptionsEvent; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\EventDispatcher\EventSubscriberInterface; /** @@ -96,7 +101,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 +117,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,6 +134,8 @@ public function deactivateGlobalButton(ActionEvent $event) } $dataDefinition = $event->getEnvironment()->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + $backendView = $dataDefinition->getDefinition(Contao2BackendViewDefinitionInterface::NAME); $globalCommands = $backendView->getGlobalCommands(); @@ -153,10 +164,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 +198,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 +219,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 +241,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,7 +265,10 @@ public function handleOriginalWidget(BuildWidgetEvent $event) $originalEvent = new BuildWidgetEvent($environment, $model, $originalProperty); - $environment->getEventDispatcher()->dispatch($originalEvent, BuildWidgetEvent::NAME); + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $dispatcher->dispatch($originalEvent, BuildWidgetEvent::NAME); $originalEvent->getWidget()->id = $event->getProperty()->getName(); $originalEvent->getWidget()->name = @@ -272,10 +296,17 @@ 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); $session = $sessionStorage->get($dataDefinition->getName() . '.' . $inputProvider->getParameter('mode')); diff --git a/src/Contao/View/Contao2BackendView/Subscriber/RichTextFileUuidSubscriber.php b/src/Contao/View/Contao2BackendView/Subscriber/RichTextFileUuidSubscriber.php index 63d5d33d6..9f0f5eecb 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; /** @@ -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 16be0f3e5..b68e266ae 100644 --- a/src/Contao/View/Contao2BackendView/Subscriber/WidgetBuilder.php +++ b/src/Contao/View/Contao2BackendView/Subscriber/WidgetBuilder.php @@ -43,10 +43,13 @@ 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. @@ -143,12 +146,12 @@ protected function getWidgetClass(PropertyInterface $property) } if (!isset($GLOBALS['BE_FFL'][$property->getWidgetType()])) { - return null; + return ''; } $className = $GLOBALS['BE_FFL'][$property->getWidgetType()]; if (!\class_exists($className)) { - return null; + return ''; } return $className; @@ -173,7 +176,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,7 +200,7 @@ 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) @@ -219,10 +226,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 +277,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 +291,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 +318,7 @@ protected function getListWizard() ' %s', StringUtil::ampersand($urlEvent->getUrl()), StringUtil::specialchars($translator->translate('importList.1', $defName)), - $importListEvent->getHtml() + $importListEvent->getHtml() ?? '' ); } @@ -310,6 +334,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 +348,12 @@ protected function getXLabel($propInfo) ) ); - $environment->getEventDispatcher()->dispatch($event, ContaoEvents::IMAGE_GET_HTML); + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); - $xLabel .= ' ' . $event->getHtml(); + $dispatcher->dispatch($event, ContaoEvents::IMAGE_GET_HTML); + + $xLabel .= ' ' . ($event->getHtml() ?? ''); } $xLabel .= $this->getHelpWizard($propInfo); @@ -355,6 +383,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 +393,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() ?? '' ); } @@ -419,8 +455,11 @@ public function buildWidget( $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 +481,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 +514,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 +549,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 d01ad303a..311035c98 100644 --- a/src/Contao/View/Contao2BackendView/TreePicker.php +++ b/src/Contao/View/Contao2BackendView/TreePicker.php @@ -36,7 +36,10 @@ 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\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,6 +50,7 @@ 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; @@ -56,6 +60,13 @@ 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; @@ -92,27 +103,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 +115,7 @@ class TreePicker extends Widget * * @var string */ - protected $sourceName; + protected $sourceName = ''; /** * The field type to use. @@ -136,13 +126,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,7 +152,7 @@ class TreePicker extends Widget * * @var string */ - protected $title; + protected $title = ''; /** * The data container for the item source. @@ -183,7 +166,7 @@ class TreePicker extends Widget * * @var string */ - protected $orderField; + protected $orderField = ''; /** * The tree nodes to be handled. @@ -205,6 +188,7 @@ 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.'); @@ -222,6 +206,7 @@ public function __construct($attributes = [], General $dataContainer = null) */ protected function setUp(General $dataContainer = null) { + /** @psalm-suppress PropertyNotSetInConstructor */ $this->dataContainer = $dataContainer ?: $this->objDca; if (!$this->dataContainer) { @@ -231,23 +216,29 @@ protected function setUp(General $dataContainer = null) $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(); } + /** @psalm-suppress PropertyNotSetInConstructor */ $this->itemContainer = (new DcGeneralFactory()) ->setContainerName($this->sourceName) ->setTranslator($environment->getTranslator()) @@ -279,10 +270,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(); @@ -327,25 +324,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; } @@ -478,11 +485,13 @@ protected function validator($value) 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 ); @@ -511,8 +520,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) @@ -580,16 +595,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,10 +642,13 @@ public function generate() */ protected function generatePickerUrl() { + $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 ]; @@ -680,21 +707,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 +754,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 +782,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 +807,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,6 +830,7 @@ private function addOrderFieldToTemplate(ContaoBackendViewTemplate $template) } $translator = $this->getEnvironment()->getTranslator(); + assert($translator instanceof TranslatorInterface); $template ->set('hasOrder', true) @@ -802,16 +854,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])) @@ -856,15 +917,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 +946,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 +959,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 +1006,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 +1027,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 +1075,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()); @@ -1021,11 +1105,22 @@ private function treeWalkChildCollection(CollectionInterface $childCollection, M */ 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(); + + $registry = $environment->getBaseConfigRegistry(); + assert($registry instanceof BaseConfigRegistryInterface); + + $rootConfig = $registry->getBaseConfig(); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $relationships = $definition->getModelRelationshipDefinition(); if (!$rootId) { $this->prepareFilterForRootCondition(); @@ -1037,6 +1132,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 +1153,17 @@ public function getTreeCollectionRecursive($rootId, $level = 0, $providerName = */ private function prepareFilterForRootCondition() { - $environment = $this->getEnvironment(); - $rootCondition = $environment->getDataDefinition()->getModelRelationshipDefinition()->getRootCondition(); + $environment = $this->getEnvironment(); + + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); - $baseConfig = $environment->getBaseConfigRegistry()->getBaseConfig(); + $rootCondition = $definition->getModelRelationshipDefinition()->getRootCondition(); + + $registry = $environment->getBaseConfigRegistry(); + assert($registry instanceof BaseConfigRegistryInterface); + + $baseConfig = $registry->getBaseConfig(); if (!$rootCondition) { return $baseConfig; } @@ -1089,12 +1192,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 +1215,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(); } @@ -1137,8 +1252,13 @@ public function loadCollection($rootId = null, $level = 0, $providerName = null) $collection = $this->getTreeCollectionRecursive($rootId, $level, $providerName); if ($rootId) { - $treeData = $environment->getDataProvider($providerName)->getEmptyCollection(); + $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 +1282,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()); @@ -1204,11 +1329,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(); + $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(); $defaultSortFields = \array_keys((array) $listing->getDefaultSortingFields()); $firstSorting = \reset($defaultSortFields); @@ -1229,7 +1358,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 +1375,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 +1383,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 +1421,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 (($maxLength = 0 !== $formatter->getMaxLength()) && \strlen($string) > $maxLength) { $string = \substr($string, 0, $maxLength); } @@ -1318,10 +1460,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( @@ -1336,7 +1481,7 @@ protected function parseModel($model, $toggleID) $template = new ContaoBackendViewTemplate('widget_treepicker_entry'); $template - ->setTranslator($this->getEnvironment()->getTranslator()) + ->setTranslator($translator) ->set('id', $this->strId) ->set('name', $this->strName) ->set('theme', Backend::getTheme()) @@ -1387,8 +1532,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) @@ -1413,15 +1561,23 @@ protected function generateTreeView($collection, $treeClass) private function parentsOf($model, &$parents) { $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 +1595,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 +1625,39 @@ 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); + + $sessionStorage = $sessionFactory->createService()->setScope($sessionKey); + assert($sessionStorage instanceof SessionStorageInterface); - $selectAction = $this->getEnvironment()->getInputProvider()->getParameter('select'); + $selectAction = $inputProvider->getParameter('select'); $session = $sessionStorage->get($tableName . '.' . $selectAction); + $propertyNamePrefix = ''; $originalPropertyName = null; + foreach ((array) $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 f67c0f887..459935cf3 100644 --- a/src/Contao/View/Contao2BackendView/TreeSelect.php +++ b/src/Contao/View/Contao2BackendView/TreeSelect.php @@ -33,11 +33,16 @@ 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\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. @@ -48,13 +53,6 @@ */ class TreeSelect { - /** - * Current ajax object. - * - * @var object - */ - protected $objAjax; - /** * The DcGeneral Object. * @@ -78,9 +76,11 @@ public function __construct() Config::getInstance(); Database::getInstance(); + /** @psalm-suppress DeprecatedMethod */ BackendUser::getInstance()->authenticate(); System::loadLanguageFile('default'); + /** @psalm-suppress DeprecatedMethod */ Backend::setStaticUrls(); } @@ -94,9 +94,13 @@ public function __construct() */ 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 +118,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 +136,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 +149,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())); } + $widgetBuilder = new WidgetBuilder($environment); + assert($widgetBuilder instanceof WidgetBuilder); + /** @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. diff --git a/src/Contao/View/Contao2BackendView/TreeView.php b/src/Contao/View/Contao2BackendView/TreeView.php index 3a05c6fb1..55797d367 100644 --- a/src/Contao/View/Contao2BackendView/TreeView.php +++ b/src/Contao/View/Contao2BackendView/TreeView.php @@ -32,16 +32,20 @@ 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\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 +54,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 +103,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 +115,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 +147,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 +171,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 +189,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 +207,7 @@ private function handleNodeStateChanges() $this->toggleModel($providerName, $modelId); } - ViewHelpers::redirectHome($this->environment); + ViewHelpers::redirectHome($environment); } } @@ -219,27 +247,38 @@ 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( $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 +307,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 +361,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 +433,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 +479,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 +511,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 +529,7 @@ public static function renderPasteRootButton(GetPasteRootButtonEvent $event) $event->getHref(), StringUtil::specialchars($label), 'onclick="Backend.getScrollOffset()"', - $imageEvent->getHtml() + $imageEvent->getHtml() ?? '' ); } @@ -473,8 +545,12 @@ 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 +572,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 +603,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 +634,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 +651,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()); } @@ -589,9 +680,12 @@ protected function formActionForSelect(ContaoBackendViewTemplate $template) */ 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 +694,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 +708,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 +720,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 +730,7 @@ public function showAll(Action $action) } /** - * Execute the multi language support. + * Execute the multi-language support. * * @param EnvironmentInterface $environment The environment. * @@ -645,10 +747,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 +774,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(); @@ -717,17 +829,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 +858,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 c92d088e0..f2eac97a1 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,15 +29,18 @@ 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. @@ -48,14 +52,17 @@ 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) { @@ -64,8 +71,11 @@ public static function getCurrentSorting(EnvironmentInterface $environment) } } + $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(); @@ -82,19 +92,22 @@ public static function getManualSortingProperty(EnvironmentInterface $environmen /** @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 */ - $definition = $sort->getSelectedDefinition(); - } + /** @var SortElementInterface $sort */ + $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 +219,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 +241,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 +271,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/FileTree.php b/src/Contao/View/Contao2BackendView/Widget/FileTree.php index 6b4b82eef..3c621f6d4 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 */ @@ -36,6 +36,10 @@ 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; /** @@ -240,7 +244,10 @@ private function setUp() return; } - $value = $this->dataContainer->getModel()->getProperty($this->orderField); + $model = $this->dataContainer->getModel(); + assert($model instanceof ModelInterface); + + $value = $model->getProperty($this->orderField); // support serialized values. if (!\is_array($value)) { @@ -262,8 +269,11 @@ protected function validator($inputValue) { if ('' === $inputValue) { 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]) ); } @@ -404,7 +414,9 @@ private function generateGalleryImage(File $file, $info) $image = $file->dataUri; } else { $projectDir = System::getContainer()->getParameter('kernel.project_dir'); - $image = System::getContainer()->get('contao.image.image_factory')->create( + assert(\is_string($projectDir)); + + $image = System::getContainer()->get('contao.image.image_factory')->create( $projectDir . '/' . $file->path, [$this->thumbnailWidth, $this->thumbnailHeight, ResizeConfiguration::MODE_BOX] )->getUrl($projectDir); @@ -490,7 +502,7 @@ public function generate() if (!empty($this->varValue)) { $files = FilesModel::findMultipleByUuids((array) $this->varValue); $this->renderList($icons, $files, $this->isGallery || $this->isDownloads); - //dump($icons); + $icons = $this->applySorting($icons); // Files can be null. @@ -541,11 +553,16 @@ public function updateAjax($ajaxAction, DataContainer $dataContainer) $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( diff --git a/src/Contao/View/Contao2BackendView/Widget/FileTreeOrder.php b/src/Contao/View/Contao2BackendView/Widget/FileTreeOrder.php index 92c8567ec..5d1b1824b 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 */ @@ -41,9 +42,7 @@ class FileTreeOrder extends AbstractWidget */ protected function validator($inputValue) { - $inputValue = \array_map('\Contao\StringUtil::uuidToBin', \array_filter(\explode(',', $inputValue))); - - return $inputValue; + return \array_map('\Contao\StringUtil::uuidToBin', \array_filter(\explode(',', $inputValue))); } /** diff --git a/src/Contao/View/Contao2BackendView/Widget/PageTree.php b/src/Contao/View/Contao2BackendView/Widget/PageTree.php index a267c7008..39a0712bb 100644 --- a/src/Contao/View/Contao2BackendView/Widget/PageTree.php +++ b/src/Contao/View/Contao2BackendView/Widget/PageTree.php @@ -20,9 +20,13 @@ 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. @@ -48,6 +52,7 @@ class PageTree extends TreePicker protected function validator($inputValue) { $translator = $this->getEnvironment()->getTranslator(); + assert($translator instanceof TranslatorInterface); $widgetValue = $this->widgetToValue($inputValue); if ((null === $widgetValue) && $this->mandatory) { @@ -70,9 +75,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 +113,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/Controller/Ajax.php b/src/Controller/Ajax.php index e32f2cbb1..c6cc249ee 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,10 +28,14 @@ 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\EnvironmentAwareInterface; +use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpFoundation\Session\SessionBagInterface; +use Symfony\Component\HttpFoundation\Session\SessionInterface; /** * Class Ajax - General purpose Ajax handler for "executePostActions" as we can not use the default Contao @@ -83,7 +88,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); } /** @@ -96,7 +104,10 @@ protected function getGet($key, $decodeEntities = false) */ protected function getPost($key, $decodeEntities = false) { - return $this->getEnvironment()->getInputProvider()->getValue($key, $decodeEntities); + $inputProvider = $this->getEnvironment()->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + + return $inputProvider->getValue($key, $decodeEntities); } /** @@ -206,7 +217,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 +272,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())); } /** @@ -287,8 +303,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 SessionBagInterface); + + $sessionBag = $sessionBag->all(); $user = BackendUser::getInstance(); diff --git a/src/Controller/Ajax3X.php b/src/Controller/Ajax3X.php index af0a2182e..bde927754 100644 --- a/src/Controller/Ajax3X.php +++ b/src/Controller/Ajax3X.php @@ -33,11 +33,14 @@ 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; /** @@ -59,15 +62,21 @@ class Ajax3X extends Ajax */ 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); @@ -102,17 +111,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 = $input->getValue('id'); $ajaxId = \preg_replace('/.*_([0-9a-zA-Z]+)$/', '$1', $rootId); $ajaxKey = \str_replace('_' . $ajaxId, '', $rootId); @@ -126,7 +138,7 @@ protected function loadPagetree() $nodes[$ajaxId] = (int) $input->getValue('state'); $session->set($ajaxKey, $nodes); - $arrData['strTable'] = $environment->getDataDefinition()->getName(); + $arrData['strTable'] = $definition->getName(); $arrData['id'] = $ajaxName ?: $rootId; $arrData['name'] = $name; @@ -150,16 +162,22 @@ 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['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 ); @@ -219,16 +237,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)); } @@ -245,9 +272,11 @@ protected function getModelFromSerializedId($serializedId) */ protected function reloadTree() { - $input = $this->getEnvironment()->getInputProvider(); - $serializedId = ($input->hasParameter('id') && $input->getParameter('id')) ? $input->getParameter('id') : null; - $value = $input->hasValue('value') ? $input->getValue('value', true) : null; + $input = $this->getEnvironment()->getInputProvider(); + assert($input instanceof InputProviderInterface); + + $serializedId = ($input->hasParameter('id') && $input->getParameter('id')) ? $input->getParameter('id') : ''; + $value = $input->hasValue('value') ? $input->getValue('value', true) : ''; $this->generateWidget($this->getWidget($this->getFieldName(), $serializedId, $value)); @@ -278,9 +307,14 @@ protected function reloadFiletree() 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 +331,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,7 +343,10 @@ private function getFieldName() } $dataDefinition = $environment->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + $sessionStorage = $environment->getSessionStorage(); + assert($sessionStorage instanceof SessionStorageInterface); $session = $sessionStorage->get($dataDefinition->getName() . '.' . $inputProvider->getParameter('select')); @@ -344,6 +382,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,7 +390,10 @@ 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(); diff --git a/src/Controller/BackendTreeController.php b/src/Controller/BackendTreeController.php index a1555437d..f0a53e3e4 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\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\DcGeneral; 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\Response; +use Symfony\Component\HttpFoundation\Session\SessionBagInterface; +use Symfony\Component\HttpFoundation\Session\SessionInterface; use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; use Symfony\Component\Routing\Annotation\Route; @@ -71,9 +80,15 @@ class BackendTreeController implements ContainerAwareInterface */ public function generalTreeAction() { - $this->container->get('contao.framework')->getAdapter(\Contao\Controller::class)->setStaticUrls(); + $container = $this->container; + assert($container instanceof ContainerInterface); - return $this->runBackendTree($this->container->get('request_stack')->getCurrentRequest()); + $framework = $container->get('contao.framework'); + assert($framework instanceof ContaoFramework); + + $framework->getAdapter(Controller::class)->setStaticUrls(); + + return $this->runBackendTree($container->get('request_stack')->getCurrentRequest()); } /** @@ -85,9 +100,15 @@ public function generalTreeAction() */ public function generalTreeToggleAction() { - $this->container->get('contao.framework')->getAdapter(\Contao\Controller::class)->setStaticUrls(); + $container = $this->container; + assert($container instanceof ContainerInterface); + + $framework = $container->get('contao.framework'); + assert($framework instanceof ContaoFramework); - return $this->runBackendTreeToggle($this->container->get('request_stack')->getCurrentRequest()); + $framework->getAdapter(Controller::class)->setStaticUrls(); + + return $this->runBackendTreeToggle($container->get('request_stack')->getCurrentRequest()); } /** @@ -99,9 +120,15 @@ public function generalTreeToggleAction() */ public function generalTreeBreadCrumbAction() { - $this->container->get('contao.framework')->getAdapter(\Contao\Controller::class)->setStaticUrls(); + $container = $this->container; + assert($container instanceof ContainerInterface); + + $framework = $container->get('contao.framework'); + assert($framework instanceof ContaoFramework); - return $this->runBackendTreeBreadCrumb($this->container->get('request_stack')->getCurrentRequest()); + $framework->getAdapter(Controller::class)->setStaticUrls(); + + return $this->runBackendTreeBreadCrumb($container->get('request_stack')->getCurrentRequest()); } /** @@ -113,9 +140,15 @@ public function generalTreeBreadCrumbAction() */ public function generalTreeUpdateAction() { - $this->container->get('contao.framework')->getAdapter(\Contao\Controller::class)->setStaticUrls(); + $container = $this->container; + assert($container instanceof ContainerInterface); + + $framework = $container->get('contao.framework'); + assert($framework instanceof ContaoFramework); + + $framework->getAdapter(Controller::class)->setStaticUrls(); - return $this->runBackendTreeUpdate($this->container->get('request_stack')->getCurrentRequest()); + return $this->runBackendTreeUpdate($container->get('request_stack')->getCurrentRequest()); } /** @@ -134,11 +167,24 @@ 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')); + + $container = $this->container; + assert($container instanceof ContainerInterface); + + $pickerBuilder = $container->get('contao.picker.builder'); + assert($pickerBuilder instanceof PickerBuilderInterface); + + $picker = $pickerBuilder->createFromData($request->query->get('picker')); + assert($picker instanceof PickerInterface); $treeSelector = $this->prepareTreeSelector($picker); - $sessionBag = $this->container->get('session')->getBag('contao_backend'); + $session = $container->get('session'); + assert($session instanceof SessionInterface); + + $sessionBag = $session->getBag('contao_backend'); + assert($sessionBag instanceof SessionBagInterface); + $sessionBag->set($treeSelector->getSearchSessionKey(), $picker->getConfig()->getValue()); $template = new ContaoBackendViewTemplate('be_main'); @@ -176,12 +222,25 @@ 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')); + + $container = $this->container; + assert($container instanceof ContainerInterface); + + $pickerBuilder = $container->get('contao.picker.builder'); + assert($pickerBuilder instanceof PickerBuilderInterface); + + $picker = $pickerBuilder->createFromData($request->query->get('picker')); + assert($picker instanceof PickerInterface); $treeSelector = $this->prepareTreeSelector($picker); $treeSelector->generatePopup(); - $sessionBag = $this->container->get('session')->getBag('contao_backend'); + $session = $container->get('session'); + assert($session instanceof SessionInterface); + + $sessionBag = $session->getBag('contao_backend'); + assert($sessionBag instanceof SessionBagInterface); + $sessionBag->set($treeSelector->getSearchSessionKey(), $picker->getConfig()->getValue()); $message = ' @@ -218,11 +277,24 @@ 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')); + + $container = $this->container; + assert($container instanceof ContainerInterface); + + $pickerBuilder = $container->get('contao.picker.builder'); + assert($pickerBuilder instanceof PickerBuilderInterface); + + $picker = $pickerBuilder->createFromData($request->query->get('picker')); + assert($picker instanceof PickerInterface); $treeSelector = $this->prepareTreeSelector($picker); - $sessionBag = $this->container->get('session')->getBag('contao_backend'); + $session = $container->get('session'); + assert($session instanceof SessionInterface); + + $sessionBag = $session->getBag('contao_backend'); + assert($sessionBag instanceof SessionBagInterface); + $sessionBag->set($treeSelector->getSearchSessionKey(), $picker->getConfig()->getValue()); $buffer = $treeSelector->generateAjax(); @@ -254,11 +326,24 @@ private function runBackendTreeUpdate(Request $request) 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')); + + $container = $this->container; + assert($container instanceof ContainerInterface); + + $pickerBuilder = $container->get('contao.picker.builder'); + assert($pickerBuilder instanceof PickerBuilderInterface); + + $picker = $pickerBuilder->createFromData($request->query->get('picker')); + assert($picker instanceof PickerInterface); $treeSelector = $this->prepareTreeSelector($picker); - $sessionBag = $this->container->get('session')->getBag('contao_backend'); + $session = $container->get('session'); + assert($session instanceof SessionInterface); + + $sessionBag = $session->getBag('contao_backend'); + assert($sessionBag instanceof SessionBagInterface); + $sessionBag->set( $treeSelector->getSearchSessionKey(), $treeSelector->widgetToValue($request->request->get('value')) @@ -266,14 +351,21 @@ private function runBackendTreeUpdate(Request $request) $modelId = ModelId::fromSerialized($picker->getConfig()->getExtra('modelId')); + $translator = $container->get('cca.translator.contao_translator'); + assert($translator instanceof TranslatorInterface); + + $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(); @@ -282,7 +374,7 @@ private function runBackendTreeUpdate(Request $request) $widgetValue = $treeSelector->widgetToValue($request->request->get('value')); if (\is_array($widgetValue)) { $values = []; - // Clean keys the have empty value. + // Clean keys they have empty value. foreach ($widgetValue as $index => $value) { if ( empty($value) @@ -300,14 +392,18 @@ private function runBackendTreeUpdate(Request $request) $propertyValues = new PropertyValueBag(); $propertyValues->setPropertyValue($picker->getConfig()->getExtra('propertyName'), $widgetValue); - $general->getEnvironment()->getController()->updateModelFromPropertyBag($model, $propertyValues); + + $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); $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; } @@ -335,21 +431,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 SessionBagInterface); + // Define the current ID. \define('CURRENT_ID', ($table ? $sessionBag->get('CURRENT_ID') : $modelId->getId())); + $translator = $container->get('cca.translator.contao_translator'); + assert($translator instanceof TranslatorInterface); + + $dispatcher = $container->get('event_dispatcher'); + assert($dispatcher instanceof EventDispatcherInterface); + $this->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 = $this->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')); diff --git a/src/Controller/ControllerInterface.php b/src/Controller/ControllerInterface.php index d8bf48ec8..dd86bfa04 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 */ @@ -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,7 +184,7 @@ 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); @@ -198,7 +198,7 @@ public function getModelsFromClipboard(ModelIdInterface $parentModelId = null); * @param FilterInterface $filter Clipboard filter. * @param array $items Write-back evaluated clipboard items. * - * @return CollectionInterface|ModelInterface[] + * @return CollectionInterface */ public function applyClipboardActions( ModelIdInterface $source = null, @@ -212,7 +212,7 @@ public function applyClipboardActions( /** * Paste the content of the clipboard onto the top. * - * @param CollectionInterface $models The models to be inserted. + * @param CollectionInterface $models The models to be inserted. * @param string $sortedBy The name of the sorting property. * @param ModelIdInterface $parentId The parent model ID. * @@ -224,7 +224,7 @@ public function pasteTop(CollectionInterface $models, $sortedBy, ModelIdInterfac * Paste the content of the clipboard after the given model. * * @param ModelInterface $previousModel The model after which to paste. - * @param CollectionInterface $models The models to be inserted. + * @param CollectionInterface $models The models to be inserted. * @param string $sortedBy The name of the sorting property. * * @return void @@ -235,7 +235,7 @@ public function pasteAfter(ModelInterface $previousModel, CollectionInterface $m * Paste the content of the clipboard into the given model. * * @param ModelInterface $parentModel The model to become the parent model of the clipboard content. - * @param CollectionInterface $models The models to be inserted. + * @param CollectionInterface $models The models to be inserted. * @param string $sortedBy The name of the sorting property. * * @return void diff --git a/src/Controller/DefaultController.php b/src/Controller/DefaultController.php index b9ed23a19..5367ed350 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,9 @@ use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralInvalidArgumentException; use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralRuntimeException; use ContaoCommunityAlliance\DcGeneral\Factory\DcGeneralFactory; +use ContaoCommunityAlliance\Translator\TranslatorInterface; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; +use function Sodium\add; /** * This class serves as main controller class in dc general. @@ -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,7 +170,11 @@ public function getEnvironment() public function handle(Action $action) { $event = new ActionEvent($this->getEnvironment(), $action); - $this->getEnvironment()->getEventDispatcher()->dispatch($event, DcGeneralEvents::ACTION); + + $dispatcher = $this->getEnvironment()->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $dispatcher->dispatch($event, DcGeneralEvents::ACTION); return $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) { @@ -189,7 +205,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) { @@ -208,7 +224,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 +241,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 +274,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,7 +306,7 @@ 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); @@ -298,8 +320,13 @@ 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); @@ -331,6 +358,7 @@ public function getSupportedLanguages($currentID) } $translator = $environment->getTranslator(); + assert($translator instanceof TranslatorInterface); // Make an array from the collection. $languages = []; @@ -388,9 +416,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 +445,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) { @@ -431,9 +464,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 +519,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 +533,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,43 +588,58 @@ 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); + + $item = $clipboard->fetch($filter)[0] ?? null; + + $action = $item ? $item->getAction() : ItemInterface::CUT; + $model = $this->modelCollector->getModel($source); - $action = $item ? $item->getAction() : ItemInterface::CUT; - $model = $this->modelCollector->getModel($source); - $actions = [ + 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(); @@ -592,15 +654,16 @@ private function fetchModelsFromClipboard(FilterInterface $filter = null, ModelI $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()) { + if (!$item->isCreate() && null !== $item->getModelId()) { $model = $this->modelCollector->getModel($item->getModelId()->getId(), $item->getDataProviderName()); } @@ -616,11 +679,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 +700,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 +726,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) { @@ -721,16 +783,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 +803,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 */ @@ -764,15 +829,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[] * - * @throws DcGeneralRuntimeException When the parameters for the pasting destination are invalid. */ private function sortAndPersistModels( array $actions, @@ -783,6 +847,7 @@ private function sortAndPersistModels( ) { /** @var DefaultCollection|ModelInterface[] $models */ $models = $this->createModelCollectionFromActions($actions, $items); + assert($models instanceof ModelInterface); $this->triggerPrePasteModel($models); @@ -801,8 +866,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 */ @@ -819,10 +884,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 */ @@ -841,10 +906,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 */ @@ -861,6 +926,7 @@ private function processPasteTopWithoutReference( ) { $manualSorting = ViewHelpers::getManualSortingProperty($this->getEnvironment()); $dataDefinition = $this->getEnvironment()->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); if (BasicDefinitionInterface::MODE_HIERARCHICAL === $dataDefinition->getBasicDefinition()->getMode()) { $this->relationshipManager->setAllRoot($models); @@ -893,6 +959,8 @@ private function processPasteTopAfterModel(CollectionInterface $models, ModelIdI } $dataProvider = $this->getEnvironment()->getDataProvider(); + assert($dataProvider instanceof DataProviderInterface); + $dataProvider->saveEach($models); $this->triggerPostPasteModel($models); @@ -930,7 +998,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 +1017,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 +1040,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 +1060,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(); + + $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 +1105,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 +1136,17 @@ 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(); - $environment->getDataProvider($models->get(0)->getProviderName())->saveEach($newList); + $dataProvider = $environment->getDataProvider($firstModel->getProviderName()); + assert($dataProvider instanceof DataProviderInterface); + + $dataProvider->saveEach($newList); } /** @@ -1061,11 +1159,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 +1176,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 { @@ -1082,10 +1186,13 @@ public function pasteAfter(ModelInterface $previousModel, CollectionInterface $m } // Enforce proper sorting now. - $siblings = $this->modelCollector->collectSiblingsOf($previousModel, $sortedBy); + $siblings = $this->modelCollector->collectSiblingsOf($previousModel, (string) $sortedBy); $newList = (new SortingManager($models, $siblings, $sortedBy, $previousModel))->getResults(); - $environment->getDataProvider($previousModel->getProviderName())->saveEach($newList); + $dataProvider = $environment->getDataProvider($previousModel->getProviderName()); + assert($dataProvider instanceof DataProviderInterface); + + $dataProvider->saveEach($newList); } /** @@ -1101,7 +1208,13 @@ public function pasteInto(ModelInterface $parentModel, CollectionInterface $mode $siblings = $this->assembleChildrenFor($parentModel, $sortedBy); $newList = (new SortingManager($models, $siblings, $sortedBy))->getResults(); - $environment->getDataProvider($newList->get(0)->getProviderName())->saveEach($newList); + $firstItem = $newList->get(0); + assert($firstItem instanceof ModelInterface); + + $dataProvider = $environment->getDataProvider($firstItem->getProviderName()); + assert($dataProvider instanceof DataProviderInterface); + + $dataProvider->saveEach($newList); } /** diff --git a/src/Controller/ModelCollector.php b/src/Controller/ModelCollector.php index 5babbb761..ff15833ba 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; @@ -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(); @@ -169,10 +176,16 @@ 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(); + assert($parentDefinition instanceof ContainerInterface); + + $dataProvider = $this->environment->getDataProvider($modelId->getDataProviderName()); + assert($dataProvider instanceof DataProviderInterface); + + $config = $dataProvider->getEmptyConfig(); if ($definition->getName() === $modelId->getDataProviderName()) { $propertyDefinition = $definition->getPropertiesDefinition(); @@ -228,8 +241,10 @@ 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)); + $result = $this->searchParentOfIn($model, $provider->fetchAll($config)); if (null !== $result) { return $result; } @@ -316,20 +331,22 @@ 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 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); @@ -339,6 +356,8 @@ public function collectSiblingsOf( // Handle grouping. $definition = $this->environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + /** @var Contao2BackendViewDefinitionInterface $viewDefinition */ $viewDefinition = $definition->getDefinition(Contao2BackendViewDefinitionInterface::NAME); if ($viewDefinition && ($viewDefinition instanceof Contao2BackendViewDefinitionInterface)) { @@ -348,7 +367,9 @@ public function collectSiblingsOf( 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 +383,10 @@ public function collectSiblingsOf( } } - return $this->environment->getDataProvider($model->getProviderName())->fetchAll($config); + $dataProvider = $this->environment->getDataProvider($model->getProviderName()); + assert($dataProvider instanceof DataProviderInterface); + + return $dataProvider->fetchAll($config); } /** @@ -422,7 +446,9 @@ 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); @@ -508,8 +534,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; diff --git a/src/Controller/SortingManager.php b/src/Controller/SortingManager.php index f65ec70b0..7d678dc48 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 */ @@ -88,16 +88,16 @@ class SortingManager /** * 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) { @@ -257,6 +257,9 @@ protected function scanToDesiredPosition() return; } + $previousModel = $this->getPreviousModel(); + assert($previousModel instanceof ModelInterface); + if ($this->siblingsCopy->length()) { // Search for "previous" sibling. do { @@ -269,7 +272,7 @@ protected function scanToDesiredPosition() if ($this->marker) { $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) { @@ -323,7 +326,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); } @@ -338,7 +341,7 @@ private function updateSorting() continue; } - $this->position += $delta; + $this->position += (int) $delta; $this->marker->setProperty($this->getSortingProperty(), $this->position); $this->results->push($this->marker); @@ -361,7 +364,10 @@ protected function calculate() } if (!$this->getSortingProperty()) { - throw new \RuntimeException('No sorting property defined for ' . $this->models->get(0)->getProviderName()); + $firstModel = $this->models->get(0); + assert($firstModel instanceof ModelInterface); + + throw new \RuntimeException('No sorting property defined for ' . $firstModel->getProviderName()); } $this->results = clone $this->models; diff --git a/src/Controller/TreeCollector.php b/src/Controller/TreeCollector.php index c0b34537f..866f2277d 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; @@ -144,7 +148,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 = []; @@ -205,8 +212,12 @@ private function getChildrenOfModel($dataProvider, $model, $childCondition) */ private function treeWalkModel(ModelInterface $model, $intLevel, $subTables = []) { - $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); @@ -234,7 +245,7 @@ private function treeWalkModel(ModelInterface $model, $intLevel, $subTables = [] if ($hasChildren) { 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,13 +275,18 @@ 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; } + assert(\is_string($basicDefinition->getParentDataProvider())); if ($basicDefinition->getParentDataProvider() !== $parentModel->getProviderName()) { throw new \RuntimeException( @@ -282,6 +298,8 @@ private function addParentFilter(ConfigInterface $config, ModelInterface $parent ); } + assert(\is_string($basicDefinition->getRootDataProvider())); + // Apply parent filtering, do this only for root elements. if ( $parentCondition = $definition->getModelRelationshipDefinition()->getChildCondition( @@ -307,9 +325,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 +349,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 +373,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(); diff --git a/src/DC/General.php b/src/DC/General.php index d91cacd69..d2f2f504b 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/DataProviderInterface.php b/src/Data/DataProviderInterface.php index f402dc37f..bf2d883c1 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 ce3322007..2af8ad704 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); } /** @@ -113,7 +113,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 +133,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 +146,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 +155,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 +177,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 +187,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/DefaultDataProvider.php b/src/Data/DefaultDataProvider.php index 9d89582a8..888823e6f 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; /** @@ -56,7 +58,7 @@ class DefaultDataProvider implements DataProviderInterface * * @var string */ - protected $source; + protected $source = ''; /** * The Database instance. @@ -462,9 +464,7 @@ public function getCount(ConfigInterface $config) $queryBuilder->from($this->source); DefaultDataProviderDBalUtils::addWhere($internalConfig, $queryBuilder); - $statement = $queryBuilder->executeQuery(); - - return $statement->fetchFirstColumn(); + return $queryBuilder->executeQuery()->fetchFirstColumn(); } /** @@ -505,7 +505,7 @@ public function resetFallback($field) ); // @codingStandardsIgnoreEnd - $this->connection->query('UPDATE ' . $this->source . ' SET ' . $field . ' = \'\'')->execute(); + $this->connection->executeQuery('UPDATE ' . $this->source . ' SET ' . $field . ' = \'\''); } /** @@ -867,6 +867,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) { diff --git a/src/Data/DefaultDataProviderDBalUtils.php b/src/Data/DefaultDataProviderDBalUtils.php index 7abf47e2b..031376c8b 100644 --- a/src/Data/DefaultDataProviderDBalUtils.php +++ b/src/Data/DefaultDataProviderDBalUtils.php @@ -49,17 +49,19 @@ public static function addField(ConfigInterface $config, $idProperty, QueryBuild if (null !== $config->getFields()) { $connection = $queryBuilder->getConnection(); - $fields = implode( + $fields = \implode( ', ', - array_map( + \array_map( function ($field) use ($connection) { return $connection->quoteIdentifier($field); }, - $config->getFields() + $config->getFields() ?? [] ) ); - if (false !== stripos($fields, 'DISTINCT') || \in_array($idProperty, $config->getFields(), true)) { + if (false !== \stripos($fields, 'DISTINCT') + || (\is_array($config->getFields()) && \in_array($idProperty, $config->getFields(), true)) + ) { $queryBuilder->select($fields); return; } @@ -107,8 +109,8 @@ public static function addSorting(ConfigInterface $config, QueryBuilder $queryBu // 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; } @@ -179,8 +181,8 @@ private static function addFilter($config, QueryBuilder $queryBuilder) */ private static function calculateSubFilter($filter, QueryBuilder $queryBuilder) { - if (!is_array($filter)) { - throw new DcGeneralRuntimeException('Error Processing sub filter: ' . var_export($filter, true), 1); + if (!\is_array($filter)) { + throw new DcGeneralRuntimeException('Error Processing sub filter: ' . \var_export($filter, true), 1); } if (null !== ($rule = static::determineFilterAndOr($filter, $queryBuilder))) { @@ -311,7 +313,7 @@ private static function filterAndOr($operation, QueryBuilder $queryBuilder) return; } - $whereOperation = strtolower($operation['operation']) . 'Where'; + $whereOperation = \strtolower($operation['operation']) . 'Where'; foreach ($children as $child) { if ('' !== $child = static::calculateSubFilter($child, $queryBuilder)) { @@ -353,7 +355,7 @@ private static function filterComparing($operation, QueryBuilder $queryBuilder) */ private static function filterInOrNotInList($operation, QueryBuilder $queryBuilder) { - $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) { @@ -381,7 +383,7 @@ private static function filterInOrNotInList($operation, QueryBuilder $queryBuild */ private static function filterLikeOrNotLike($operation, QueryBuilder $queryBuilder) { - $wildcards = str_replace(array('*', '?'), array('%', '_'), $operation['value']); + $wildcards = \str_replace(array('*', '?'), array('%', '_'), $operation['value']); return $queryBuilder ->expr() @@ -402,7 +404,7 @@ private static function filterLikeOrNotLike($operation, QueryBuilder $queryBuild */ private static function filterIsNullOrIsNotNull($operation, QueryBuilder $queryBuilder) { - $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 2faadf72b..ea31b2967 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"). @@ -267,6 +267,6 @@ private static function filterLike($operation, &$params) $wildcards = \str_replace(['*', '?'], ['%', '_'], $operation['value']); $params[] = $wildcards; - return \sprintf('(%s LIKE ?)', $operation['property'], $wildcards); + return \sprintf('(%s LIKE %s)', $operation['property'], $wildcards); } } diff --git a/src/Data/DefaultLanguageInformationCollection.php b/src/Data/DefaultLanguageInformationCollection.php index ab9ad2302..e03a17c86 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 852459183..abc1cf876 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 @@ -41,7 +42,7 @@ class DefaultModel extends AbstractModel 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); } } diff --git a/src/Data/LanguageInformationCollectionInterface.php b/src/Data/LanguageInformationCollectionInterface.php index 3b9d34d47..04e8600bf 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/ModelInterface.php b/src/Data/ModelInterface.php index 6bfe12513..1d9b37779 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,11 +118,11 @@ 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. @@ -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 */ @@ -153,21 +156,21 @@ public function setId($mixId); /** * Update the property value in the model. * - * @param string $strPropertyName The property name to be set. - * @param mixed $varValue The value to be set. + * @param string $strStrPropertyName The property name to be set. + * @param mixed $varVarValue The value to be set. * * @return void */ - public function setProperty($strPropertyName, $varValue); + public function setProperty($strStrPropertyName, $varVarValue); /** * 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/MultiLanguageDataProviderInterface.php b/src/Data/MultiLanguageDataProviderInterface.php index bbdd5e698..4759735c6 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/PropertyValueBagInterface.php b/src/Data/PropertyValueBagInterface.php index 100b5f307..9912d41da 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 { diff --git a/src/Data/TableRowsAsRecordsDataProvider.php b/src/Data/TableRowsAsRecordsDataProvider.php index 86fe9a2a4..3342215d2 100644 --- a/src/Data/TableRowsAsRecordsDataProvider.php +++ b/src/Data/TableRowsAsRecordsDataProvider.php @@ -262,9 +262,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); diff --git a/src/DataDefinition/AbstractConditionChain.php b/src/DataDefinition/AbstractConditionChain.php index 7facf388c..1deda72f6 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 2fc3bb393..c2a152916 100644 --- a/src/DataDefinition/Builder/AbstractEventDrivenDataDefinitionBuilder.php +++ b/src/DataDefinition/Builder/AbstractEventDrivenDataDefinitionBuilder.php @@ -53,7 +53,7 @@ abstract class AbstractEventDrivenDataDefinitionBuilder implements DataDefinitio * * @var string */ - protected $eventName; + protected $eventName = ''; /** * Retrieve the dispatcher. diff --git a/src/DataDefinition/DataProviderInformation.php b/src/DataDefinition/DataProviderInformation.php index adb9f287a..457788f87 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 fd77d85a2..b957c0222 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; } /** diff --git a/src/DataDefinition/Definition/DataProviderDefinitionInterface.php b/src/DataDefinition/Definition/DataProviderDefinitionInterface.php index 61b93c42b..1d37129d5 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/DefaultModelRelationshipDefinition.php b/src/DataDefinition/Definition/DefaultModelRelationshipDefinition.php index 151ae9cfc..3dc2c01ad 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 */ @@ -110,9 +111,7 @@ public function getChildConditions($srcProvider = '') */ public function __clone() { - if (null !== $this->rootCondition) { - $this->rootCondition = clone $this->rootCondition; - } + $this->rootCondition = clone $this->rootCondition; $conditions = []; foreach ($this->childConditions as $condition) { diff --git a/src/DataDefinition/Definition/Properties/DefaultProperty.php b/src/DataDefinition/Definition/Properties/DefaultProperty.php index afb822805..316d0b53d 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 d166d0063..825e374ab 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 2c72c6b3d..9876193c5 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. diff --git a/src/DataDefinition/Definition/View/CommandCollection.php b/src/DataDefinition/Definition/View/CommandCollection.php index 994ba9a81..d1c3a0a48 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 b78e9ef5d..dd9f2bcb8 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 f0faf407a..bf40b27a6 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/DefaultPanelRowCollection.php b/src/DataDefinition/Definition/View/DefaultPanelRowCollection.php index cb1bc81eb..1ccfca0a3 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/PanelRowCollectionInterface.php b/src/DataDefinition/Definition/View/PanelRowCollectionInterface.php index ec9d33d39..99157b28f 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 { diff --git a/src/DataDefinition/Definition/View/PanelRowInterface.php b/src/DataDefinition/Definition/View/PanelRowInterface.php index b3a754798..fc0a0bf33 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 { diff --git a/src/DataDefinition/ModelRelationship/FilterBuilder/BaseComparingFilterBuilder.php b/src/DataDefinition/ModelRelationship/FilterBuilder/BaseComparingFilterBuilder.php index 2472c7a59..8cc2bcc38 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. diff --git a/src/DataDefinition/ModelRelationship/FilterBuilder/FilterBuilderWithChildren.php b/src/DataDefinition/ModelRelationship/FilterBuilder/FilterBuilderWithChildren.php index 47d364ec5..9362a0e14 100644 --- a/src/DataDefinition/ModelRelationship/FilterBuilder/FilterBuilderWithChildren.php +++ b/src/DataDefinition/ModelRelationship/FilterBuilder/FilterBuilderWithChildren.php @@ -30,6 +30,9 @@ * * 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 diff --git a/src/DataDefinition/ModelRelationship/ParentChildCondition.php b/src/DataDefinition/ModelRelationship/ParentChildCondition.php index 0acec1122..037b799ed 100644 --- a/src/DataDefinition/ModelRelationship/ParentChildCondition.php +++ b/src/DataDefinition/ModelRelationship/ParentChildCondition.php @@ -46,35 +46,35 @@ 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 */ - private $neededProperties; + private $neededProperties = []; /** * {@inheritdoc} @@ -123,7 +123,7 @@ public function setInverseFilterArray($value) return $this; } - $this->inverseFilter = (array) $value; + $this->inverseFilter = $value; return $this; } @@ -140,7 +140,7 @@ public function getInverseFilterArray() */ public function setSourceName($value) { - $this->sourceProvider = (string) $value; + $this->sourceProvider = $value; return $this; } @@ -158,7 +158,7 @@ public function getSourceName() */ public function setDestinationName($value) { - $this->destinationProvider = (string) $value; + $this->destinationProvider = $value; return $this; } diff --git a/src/DataDefinition/ModelRelationship/ParentChildConditionInterface.php b/src/DataDefinition/ModelRelationship/ParentChildConditionInterface.php index d3d485389..ded792cf2 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. diff --git a/src/DataDefinition/ModelRelationship/RootCondition.php b/src/DataDefinition/ModelRelationship/RootCondition.php index 46fe84594..d2a18d2d5 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} diff --git a/src/DataDefinition/ModelRelationship/RootConditionInterface.php b/src/DataDefinition/ModelRelationship/RootConditionInterface.php index 1aedace32..67c07fc2b 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/PaletteBuilder.php b/src/DataDefinition/Palette/Builder/PaletteBuilder.php index e80c921f8..b735b12f1 100644 --- a/src/DataDefinition/Palette/Builder/PaletteBuilder.php +++ b/src/DataDefinition/Palette/Builder/PaletteBuilder.php @@ -75,6 +75,7 @@ use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\PropertyInterface; use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralInvalidArgumentException; use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralRuntimeException; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; /** * The palette builder is used to build palette collections, palettes, legends, properties and conditions. @@ -1309,6 +1310,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/PropertyValueCondition.php b/src/DataDefinition/Palette/Condition/Palette/PropertyValueCondition.php index c29f43205..c6c2843aa 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 b322c32b1..889dd176d 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 14798ea71..8a04ce575 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/PropertyEditableCondition.php b/src/DataDefinition/Palette/Condition/Property/PropertyEditableCondition.php index e8b030c3b..772153920 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 */ @@ -24,6 +25,7 @@ use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; use ContaoCommunityAlliance\DcGeneral\Data\PropertyValueBag; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\LegendInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\PaletteInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\PropertyInterface; /** @@ -45,7 +47,7 @@ class PropertyEditableCondition implements PropertyConditionInterface */ public function __construct($propertyName = '') { - $this->propertyName = (string) $propertyName; + $this->propertyName = $propertyName; } /** @@ -57,7 +59,7 @@ public function __construct($propertyName = '') */ public function setPropertyName($propertyName) { - $this->propertyName = (string) $propertyName; + $this->propertyName = $propertyName; return $this; } @@ -84,8 +86,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 7dfb9361c..43010cc00 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,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 */ @@ -53,8 +53,8 @@ class PropertyFalseCondition implements PropertyConditionInterface */ public function __construct($propertyName, $strict = false) { - $this->propertyName = (string) $propertyName; - $this->strict = (bool) $strict; + $this->propertyName = $propertyName; + $this->strict = $strict; } /** @@ -66,7 +66,7 @@ public function __construct($propertyName, $strict = false) */ public function setPropertyName($propertyName) { - $this->propertyName = (string) $propertyName; + $this->propertyName = $propertyName; return $this; } @@ -90,7 +90,7 @@ public function getPropertyName() */ public function setStrict($strict) { - $this->strict = (bool) $strict; + $this->strict = $strict; return $this; } diff --git a/src/DataDefinition/Palette/Condition/Property/PropertyTrueCondition.php b/src/DataDefinition/Palette/Condition/Property/PropertyTrueCondition.php index 156d8fd56..49f530e65 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 */ @@ -53,8 +54,8 @@ class PropertyTrueCondition implements PropertyConditionInterface */ public function __construct($propertyName = '', $strict = false) { - $this->propertyName = (string) $propertyName; - $this->strict = (bool) $strict; + $this->propertyName = $propertyName; + $this->strict = $strict; } /** diff --git a/src/DataDefinition/Palette/Condition/Property/PropertyValueCondition.php b/src/DataDefinition/Palette/Condition/Property/PropertyValueCondition.php index e55fb4c04..a37c45dca 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 ebcba4dc1..0f0ba0358 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 2d96ce052..ee076f06a 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 */ @@ -95,7 +96,7 @@ public function getPalette() */ public function setName($name) { - $this->name = (string) $name; + $this->name = $name; return $this; } @@ -112,7 +113,7 @@ public function getName() */ public function setInitialVisibility($value) { - $this->initiallyVisible = (bool) $value; + $this->initiallyVisible = $value; return $this; } @@ -180,9 +181,9 @@ public function addProperty(PropertyInterface $property, PropertyInterface $befo $position = \array_search($beforeHash, $hashes); $this->properties = \array_merge( - \array_slice($this->properties, 0, $position), + \array_slice($this->properties, 0, $position ?? null), [$hash => $property], - \array_slice($this->properties, $position) + \array_slice($this->properties, $position ?? 0) ); return $this; diff --git a/src/DataDefinition/Palette/Palette.php b/src/DataDefinition/Palette/Palette.php index 3f2f61154..52fb66180 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,7 +38,7 @@ class Palette implements PaletteInterface * * @var string */ - protected $name; + protected $name = ''; /** * List of all legends in this palette. @@ -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/Property.php b/src/DataDefinition/Palette/Property.php index 74ec45560..b013a5068 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 */ @@ -40,16 +41,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 +67,7 @@ public function __construct($name) */ public function setName($name) { - $this->name = (string) $name; + $this->name = $name; } /** diff --git a/src/DataDefinition/Palette/PropertyInterface.php b/src/DataDefinition/Palette/PropertyInterface.php index 82896db76..140ec625c 100644 --- a/src/DataDefinition/Palette/PropertyInterface.php +++ b/src/DataDefinition/Palette/PropertyInterface.php @@ -96,7 +96,7 @@ public function setVisibleCondition(PropertyConditionInterface $condition = null /** * Get the visible condition for this property. * - * @return PropertyConditionInterface + * @return PropertyConditionInterface|null */ public function getVisibleCondition(); @@ -112,7 +112,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/DefaultEnvironment.php b/src/DefaultEnvironment.php index b8e133aed..d535b9fcf 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,12 +340,12 @@ public function getClipboard() /** * {@inheritdoc} */ - public function setClipboard($clipboard) + public function setClipboard($objClipboard) { - if (null === $clipboard) { + if (null === $objClipboard) { $this->objClipboard = null; } else { - $this->objClipboard = $clipboard; + $this->objClipboard = $objClipboard; } return $this; @@ -345,13 +354,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/EnvironmentInterface.php b/src/EnvironmentInterface.php index 32d0e9fdc..8474b9545 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. @@ -181,30 +184,30 @@ public function hasDataProvider($strSource = null); * If a source name is given, the named data provider will get returned, if not given, the default data provider * will get returned, the default is to be determined via: getEnvironment()->getDataDefinition()->getDataProvider() * - * @param string|null $strSource The name of the source. + * @param string|null $strStrSource The name of the source. * * @return DataProviderInterface|null */ - public function getDataProvider($strSource = null); + public function getDataProvider($strStrSource = null); /** * Register a data provider to the environment. * - * @param string $strSource The name of the source. + * @param string $strStrSource The name of the source. * @param DataProviderInterface $dataProvider The data provider instance to register under the given name. * * @return EnvironmentInterface */ - public function addDataProvider($strSource, $dataProvider); + public function addDataProvider($strStrSource, $dataProvider); /** * Remove a data provider from the environment. * - * @param string $strSource The name of the source. + * @param string $strStrSource The name of the source. * * @return EnvironmentInterface */ - public function removeDataProvider($strSource); + public function removeDataProvider($strStrSource); /** * Return the clipboard. @@ -216,11 +219,11 @@ public function getClipboard(); /** * Set the the clipboard. * - * @param ClipboardInterface $objClipboard Clipboard instance. + * @param ClipboardInterface $objObjClipboard Clipboard instance. * * @return EnvironmentInterface */ - public function setClipboard($objClipboard); + public function setClipboard($objObjClipboard); /** * Set the translation manager to use. diff --git a/src/EventListener/ModelRelationship/ParentEnforcingListener.php b/src/EventListener/ModelRelationship/ParentEnforcingListener.php index 59e0d5df8..0a7c03fab 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,10 @@ use ContaoCommunityAlliance\DcGeneral\Controller\ModelCollector; use ContaoCommunityAlliance\DcGeneral\Controller\RelationshipManager; use ContaoCommunityAlliance\DcGeneral\Data\ModelId; +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 +44,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; diff --git a/src/EventListener/ModelRelationship/TreeEnforcingListener.php b/src/EventListener/ModelRelationship/TreeEnforcingListener.php index 3fae9e259..abbfbf50b 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 */ @@ -25,8 +26,10 @@ 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 tree relationship on a model. @@ -43,17 +46,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 ); @@ -129,6 +138,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 1fe0e6f79..b252b0c30 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 */ @@ -84,11 +84,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,7 +113,11 @@ 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; } @@ -169,7 +182,7 @@ public static function deriveFromEnvironment(EnvironmentInterface $environment) */ public function setEnvironmentClassName($environmentClassName) { - $this->environmentClassName = (string) $environmentClassName; + $this->environmentClassName = $environmentClassName; return $this; } @@ -187,7 +200,7 @@ public function getEnvironmentClassName() */ public function setContainerName($containerName) { - $this->containerName = (string) $containerName; + $this->containerName = $containerName; return $this; } @@ -205,7 +218,7 @@ public function getContainerName() */ public function setContainerClassName($containerClassName) { - $this->containerClassName = (string) $containerClassName; + $this->containerClassName = $containerClassName; return $this; } @@ -223,7 +236,7 @@ public function getContainerClassName() */ public function setDcGeneralClassName($dcGeneralClassName) { - $this->dcGeneralClassName = (string) $dcGeneralClassName; + $this->dcGeneralClassName = $dcGeneralClassName; return $this; } @@ -324,7 +337,10 @@ 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); @@ -401,6 +417,7 @@ public function createContainer() /** @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/Panel/AbstractElement.php b/src/Panel/AbstractElement.php index 41ac490fe..a2069980a 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; @@ -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; } /** @@ -88,9 +96,9 @@ public function getPanel() /** * {@inheritDoc} */ - public function setPanel(PanelInterface $panel) + public function setPanel(PanelInterface $panelElement) { - $this->objPanel = $panel; + $this->objPanel = $panelElement; return $this; } @@ -103,10 +111,10 @@ public function setPanel(PanelInterface $panel) protected function getOtherConfig() { if (!isset($this->objOtherConfig)) { - $this->objOtherConfig = $this - ->getEnvironment() - ->getBaseConfigRegistry() - ->getBaseConfig(); + $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 9409a03d2..328e5e132 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 = [ diff --git a/src/Panel/DefaultLimitElement.php b/src/Panel/DefaultLimitElement.php index fb513b21c..b7b9e153b 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,10 +85,11 @@ protected function getMaxItemsPerPage() protected function calculateTotal() { $otherConfig = $this->getOtherConfig(); - $total = $this - ->getEnvironment() - ->getDataProvider() - ->fetchAll($otherConfig->setIdOnly(true)); + + $dataProvider = $this->getEnvironment()->getDataProvider(); + assert($dataProvider instanceof DataProviderInterface); + + $total = $dataProvider->fetchAll($otherConfig->setIdOnly(true)); if (\is_array($total)) { $this->intTotal = $total ? \count($total) : 0; @@ -114,8 +118,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 +138,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 +182,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 +196,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 +208,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 +303,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/DefaultPanelContainer.php b/src/Panel/DefaultPanelContainer.php index 71ae322a1..389be0690 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. @@ -40,9 +41,9 @@ class DefaultPanelContainer implements PanelContainerInterface /** * The panels contained within this container. * - * @var PanelInterface[] + * @var array */ - private $arrPanels = []; + private array $panels = []; /** * {@inheritdoc} @@ -66,7 +67,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 +78,7 @@ public function addPanel($panelName, $panel) */ public function getPanel($panelName) { - return $this->arrPanels[$panelName]; + return $this->panels[$panelName]; } /** @@ -96,7 +97,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 +108,7 @@ public function updateValues() */ public function getIterator(): \Traversable { - return new \ArrayIterator($this->arrPanels); + return new \ArrayIterator($this->panels); } /** @@ -112,6 +116,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 5f11d57a2..04c25b3f3 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; /** @@ -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'); } @@ -109,14 +117,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); - if ('1' !== $this->getEnvironment()->getInputProvider()->getValue('filter_reset')) { + $value = null; + $field = null; + + 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 +143,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 +174,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 +201,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 +219,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 22346d940..6b14d2f54 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,8 +27,10 @@ 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_; @@ -51,10 +53,11 @@ class DefaultSortElement extends AbstractElement implements SortElementInterface */ 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() @@ -86,13 +89,16 @@ protected function searchDefinitionByName($name) */ 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 []; @@ -107,8 +113,12 @@ 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'); @@ -130,9 +140,9 @@ 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(); @@ -162,9 +172,11 @@ private function defineSortOption(?PanelElementInterface $panelElement): void } $input = $this->getInputProvider(); - $value = null; + assert($input instanceof InputProviderInterface); + + $value = ''; - if ('1' !== $this->getEnvironment()->getInputProvider()->getValue('filter_reset')) { + if ('1' !== $input->getValue('filter_reset')) { if ($input->hasValue('tl_sort') && $this->getPanel()->getContainer()->updateValues()) { $value = $input->getValue('tl_sort'); @@ -181,7 +193,7 @@ private function defineSortOption(?PanelElementInterface $panelElement): void $this->setSelected($persistent); } else { - $this->setPersistent(null); + $this->setPersistent(''); } } @@ -190,11 +202,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 7e6ffd5b1..8448d558a 100644 --- a/src/Panel/DefaultSubmitElement.php +++ b/src/Panel/DefaultSubmitElement.php @@ -32,7 +32,7 @@ class DefaultSubmitElement extends AbstractElement implements SubmitElementInter /** * {@inheritdoc} */ - public function initialize(ConfigInterface $config, PanelElementInterface $panelElement = null) + public function initialize(ConfigInterface $config, PanelElementInterface $element = null) { } diff --git a/src/Panel/PanelContainerInterface.php b/src/Panel/PanelContainerInterface.php index d59602a59..b38309fda 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 3cef7230a..4ac081a21 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 */ - public function render(ViewTemplateInterface $objTemplate); + public function render(ViewTemplateInterface $viewTemplate); } diff --git a/src/Panel/PanelInterface.php b/src/Panel/PanelInterface.php index 4d8531057..6305ecf95 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 */ - 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/Resources/config/contao/picker_provider.yml b/src/Resources/config/contao/picker_provider.yml index 5a7765fe3..03964f72c 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 12f120c47..5665d9403 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/AbstractPropertyOverrideEditAllHandler.php b/src/View/ActionHandler/AbstractPropertyOverrideEditAllHandler.php index 7b2a60514..c929bbe31 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. @@ -69,6 +73,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 +115,7 @@ protected function editCollection( ) { while ($collection->count() > 0) { $model = $collection->shift(); + assert($model instanceof ModelInterface); $persistPropertyValueBag = $this->cloneCleanPropertyValueBag($action, $propertyValueBag, $model, $environment); @@ -232,6 +238,8 @@ 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) { return; @@ -301,7 +309,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 +321,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 +360,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); @@ -407,6 +422,7 @@ private function handleInputValues(Action $action, ModelInterface $model, Enviro } $inputProvider = $this->getInputProvider($environment); + assert($inputProvider instanceof InputProviderInterface); $inputValues = []; foreach (\array_keys($_POST) as $valueName) { @@ -484,6 +500,7 @@ protected function restoreInputValues( protected function getEditButtons(Action $action, EnvironmentInterface $environment) { $translator = $environment->getTranslator(); + assert($translator instanceof TranslatorInterface); $mode = $this->getMode($action); @@ -548,7 +565,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 +613,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') @@ -644,7 +664,7 @@ protected function getEditPropertiesByModelId( EnvironmentInterface $environment ) { $session = $this->getSession($action, $environment); -//dd($session['editProperties']); + return $session['editProperties'][$modelId->getSerialized()] ?? []; } @@ -660,8 +680,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 +715,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 +736,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 +770,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 +798,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/ViewTemplateInterface.php b/src/View/ViewTemplateInterface.php index 9941d11cc..7ddec17d3 100644 --- a/src/View/ViewTemplateInterface.php +++ b/src/View/ViewTemplateInterface.php @@ -31,7 +31,7 @@ interface ViewTemplateInterface * * @param array $data The data array. * - * @return ViewTemplateInterface + * @return self */ public function setData($data); @@ -48,7 +48,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);