From 26b81f065eb85749a98f50388eb4a9ee22dabde1 Mon Sep 17 00:00:00 2001 From: Ingolf Steinardt Date: Thu, 11 Apr 2024 07:46:39 +0200 Subject: [PATCH 1/5] Add translation keys for MM support We can use `.label` ans `.description` instead of `.0` and `.1` --- .../Legacy/LegacyDcaDataDefinitionBuilder.php | 2 +- .../AbstractListShowAllHandler.php | 22 +++++++++++++++++++ .../ParentedListViewShowAllHandler.php | 18 ++++++++++----- src/Panel/DefaultFilterElement.php | 13 ++++++----- src/Panel/DefaultSearchElement.php | 12 +++++----- src/Panel/DefaultSortElement.php | 9 +++++--- 6 files changed, 55 insertions(+), 21 deletions(-) diff --git a/src/Contao/Dca/Builder/Legacy/LegacyDcaDataDefinitionBuilder.php b/src/Contao/Dca/Builder/Legacy/LegacyDcaDataDefinitionBuilder.php index 5a5bbd1b..36707e75 100644 --- a/src/Contao/Dca/Builder/Legacy/LegacyDcaDataDefinitionBuilder.php +++ b/src/Contao/Dca/Builder/Legacy/LegacyDcaDataDefinitionBuilder.php @@ -1332,7 +1332,7 @@ protected function parseSingleProperty(PropertyInterface $property, array $propI switch ($key) { case 'label': if (null === $value) { - $value = ''; + $value = 'LABEL NOT SET: ' . $property->getName(); } assert(is_string($value) || is_array($value)); $this->parseSinglePropertyLabel($property, $value); diff --git a/src/Contao/View/Contao2BackendView/ActionHandler/AbstractListShowAllHandler.php b/src/Contao/View/Contao2BackendView/ActionHandler/AbstractListShowAllHandler.php index c4ba472e..bb77daa7 100644 --- a/src/Contao/View/Contao2BackendView/ActionHandler/AbstractListShowAllHandler.php +++ b/src/Contao/View/Contao2BackendView/ActionHandler/AbstractListShowAllHandler.php @@ -344,6 +344,28 @@ protected function translate($key, $domain, array $parameters = []) return $translated; } + protected function translateButtonLabel(string $buttonName, $definitionName): string + { + // New way via symfony translator. + if ($buttonName . '.label' !== ($header = $this->translate($buttonName . '.label', $definitionName))) { + return $header; + } + + // FIXME: Fallback to legacy translator. + return $this->translate($buttonName . '.0', $definitionName); + } + + protected function translateButtonDescription(string $buttonName, $definitionName): string + { + // New way via symfony translator. + if ($buttonName . '.description' !== ($header = $this->translate($buttonName . '.description', $definitionName))) { + return $header; + } + + // FIXME: Fallback to legacy translator. + return $this->translate($buttonName . '.1', $definitionName); + } + /** * Render a model. * diff --git a/src/Contao/View/Contao2BackendView/ActionHandler/ParentedListViewShowAllHandler.php b/src/Contao/View/Contao2BackendView/ActionHandler/ParentedListViewShowAllHandler.php index 67176cc3..f816f04a 100644 --- a/src/Contao/View/Contao2BackendView/ActionHandler/ParentedListViewShowAllHandler.php +++ b/src/Contao/View/Contao2BackendView/ActionHandler/ParentedListViewShowAllHandler.php @@ -232,9 +232,15 @@ function ($value) { */ private function translateHeaderColumnName($field, $parentName) { + // New way via symfony translator. + if ($field . '.label' !== ($header = $this->translate($field . '.label', $parentName))) { + return $header; + } + // FIXME: deprecation here? - old translation handling. + return ('tstamp' === $field) - ? $this->translate('tstamp', 'dc-general') - : $this->translate(\sprintf('%s.0', $field), $parentName); + ? $this->translate('tstamp', 'contao_dc-general') + : $this->translate(\sprintf('%s.0', $field), 'contao_' . $parentName); } /** @@ -294,8 +300,8 @@ private function renderForCheckbox(PropertyInterface $property, $value, &$isRend $isRendered = true; return !empty($value) - ? $this->translate('yes', 'dc-general') - : $this->translate('no', 'dc-general'); + ? $this->translate('yes', 'contao_dc-general') + : $this->translate('no', 'contao_dc-general'); } /** @@ -462,7 +468,7 @@ protected function getHeaderEditButton(ModelInterface $parentModel, EnvironmentI $imageEvent = $dispatcher->dispatch( new GenerateHtmlEvent( 'edit.svg', - $this->translate('editheader.0', $parentDefinition->getName()) + $this->translateButtonLabel('editheader', $parentDefinition->getName()) ), ContaoEvents::IMAGE_GET_HTML ); @@ -478,7 +484,7 @@ protected function getHeaderEditButton(ModelInterface $parentModel, EnvironmentI '%s', $urlAfter->getUrl(), StringUtil::specialchars( - \sprintf($this->translate('editheader.1', $parentDefinition->getName()), $parentModel->getId()) + \sprintf($this->translateButtonDescription('editheader', $parentDefinition->getName()), $parentModel->getId()) ), $imageEvent->getHtml() ?? '' ); diff --git a/src/Panel/DefaultFilterElement.php b/src/Panel/DefaultFilterElement.php index 2509cc89..612f131f 100644 --- a/src/Panel/DefaultFilterElement.php +++ b/src/Panel/DefaultFilterElement.php @@ -31,6 +31,7 @@ use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; use ContaoCommunityAlliance\DcGeneral\SessionStorageInterface; use ContaoCommunityAlliance\DcGeneral\View\ViewTemplateInterface; +use ContaoCommunityAlliance\Translator\TranslatorInterface; /** * Default implementation of a filter panel element. @@ -209,13 +210,15 @@ public function render(ViewTemplateInterface $viewTemplate) { $definition = $this->getEnvironment()->getDataDefinition(); assert($definition instanceof ContainerInterface); - - $labels = $definition->getPropertiesDefinition() - ->getProperty($this->getPropertyName())->getLabel(); + $translator = $this->getEnvironment()->getTranslator(); + assert($translator instanceof TranslatorInterface); + $properties = $definition->getPropertiesDefinition(); + $field = $this->getPropertyName(); + $label = $translator->translate($properties->getProperty($field)->getLabel(), $definition->getName()); $options = [ [ - 'value' => 'tl_' . $this->getPropertyName(), + 'value' => 'tl_' . $field, 'content' => '---', 'attributes' => '' ] @@ -230,7 +233,7 @@ public function render(ViewTemplateInterface $viewTemplate) ]; } - $viewTemplate->set('label', $labels); + $viewTemplate->set('label', $label); $viewTemplate->set('name', $this->getPropertyName()); $viewTemplate->set('id', $this->getPropertyName()); $viewTemplate->set('class', 'tl_select' . ((null !== $selectedValue) ? ' active' : '')); diff --git a/src/Panel/DefaultSearchElement.php b/src/Panel/DefaultSearchElement.php index 72f3d2dd..e93ec004 100644 --- a/src/Panel/DefaultSearchElement.php +++ b/src/Panel/DefaultSearchElement.php @@ -27,6 +27,7 @@ use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; use ContaoCommunityAlliance\DcGeneral\View\ViewTemplateInterface; +use ContaoCommunityAlliance\Translator\TranslatorInterface; /** * Default implementation of a search panel element. @@ -174,15 +175,14 @@ public function render(ViewTemplateInterface $viewTemplate) assert($definition instanceof ContainerInterface); $options = []; - + $translator = $this->getEnvironment()->getTranslator(); + assert($translator instanceof TranslatorInterface); + $properties = $definition->getPropertiesDefinition(); foreach ($this->getPropertyNames() as $field) { - $lLabels = $definition - ->getPropertiesDefinition() - ->getProperty($field) - ->getLabel(); + $label = $translator->translate($properties->getProperty($field)->getLabel(), $definition->getName()); $options[] = [ 'value' => $field, - 'content' => $lLabels, + 'content' => $label, 'attributes' => ($field === $this->getSelectedProperty()) ? ' selected' : '' ]; } diff --git a/src/Panel/DefaultSortElement.php b/src/Panel/DefaultSortElement.php index 3f5b83a5..956767d9 100644 --- a/src/Panel/DefaultSortElement.php +++ b/src/Panel/DefaultSortElement.php @@ -32,6 +32,7 @@ use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\GroupAndSortingDefinitionInterface; use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; use ContaoCommunityAlliance\DcGeneral\View\ViewTemplateInterface; +use ContaoCommunityAlliance\Translator\TranslatorInterface; /** * Default implementation of a sort element. @@ -195,14 +196,16 @@ public function render(ViewTemplateInterface $viewTemplate) { $definition = $this->getEnvironment()->getDataDefinition(); assert($definition instanceof ContainerInterface); + $properties = $definition->getPropertiesDefinition(); + $translator = $this->getEnvironment()->getTranslator(); + assert($translator instanceof TranslatorInterface); $options = []; foreach ($this->getGroupAndSortingDefinition() as $information) { /** @var GroupAndSortingDefinitionInterface $information */ - $name = $information->getName(); - $properties = $definition->getPropertiesDefinition(); + $name = $information->getName(); if ($properties->hasProperty($name)) { - $name = $properties->getProperty($name)->getLabel(); + $name = $translator->translate($properties->getProperty($name)->getLabel(), $definition->getName()); } if (empty($name)) { From bb6ddc0809187047769c7b3e82b107daee9991da Mon Sep 17 00:00:00 2001 From: Ingolf Steinardt Date: Thu, 11 Apr 2024 07:59:19 +0200 Subject: [PATCH 2/5] Fix PHPCQ2 --- .../ActionHandler/AbstractListShowAllHandler.php | 5 ++++- .../ActionHandler/ParentedListViewShowAllHandler.php | 9 ++++++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/Contao/View/Contao2BackendView/ActionHandler/AbstractListShowAllHandler.php b/src/Contao/View/Contao2BackendView/ActionHandler/AbstractListShowAllHandler.php index bb77daa7..b1519198 100644 --- a/src/Contao/View/Contao2BackendView/ActionHandler/AbstractListShowAllHandler.php +++ b/src/Contao/View/Contao2BackendView/ActionHandler/AbstractListShowAllHandler.php @@ -358,7 +358,10 @@ protected function translateButtonLabel(string $buttonName, $definitionName): st protected function translateButtonDescription(string $buttonName, $definitionName): string { // New way via symfony translator. - if ($buttonName . '.description' !== ($header = $this->translate($buttonName . '.description', $definitionName))) { + if ( + $buttonName . '.description' + !== ($header = $this->translate($buttonName . '.description', $definitionName)) + ) { return $header; } diff --git a/src/Contao/View/Contao2BackendView/ActionHandler/ParentedListViewShowAllHandler.php b/src/Contao/View/Contao2BackendView/ActionHandler/ParentedListViewShowAllHandler.php index f816f04a..4cd50ff4 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-2023 Contao Community Alliance. + * (c) 2013-2024 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-2023 Contao Community Alliance. + * @copyright 2013-2024 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -484,7 +484,10 @@ protected function getHeaderEditButton(ModelInterface $parentModel, EnvironmentI '%s', $urlAfter->getUrl(), StringUtil::specialchars( - \sprintf($this->translateButtonDescription('editheader', $parentDefinition->getName()), $parentModel->getId()) + \sprintf( + $this->translateButtonDescription('editheader', $parentDefinition->getName()), + $parentModel->getId() + ) ), $imageEvent->getHtml() ?? '' ); From 29999a371c214c205f3cdd121460b4aec1fd26ae Mon Sep 17 00:00:00 2001 From: Ingolf Steinardt Date: Wed, 17 Apr 2024 19:28:40 +0200 Subject: [PATCH 3/5] Update for translation handling and support arbitrary routing - Translate operation button labels and descriptions correctly including fallback to old style translation files - Translate global operation button labels and descriptions correctly including fallback to old style translation files - Correct translation key for editAll button - Fix URL generating in CopyHandler - Translate property names in SelectAllHandler correctly - Translate property names in parent view header correctly - Change various translation keys from .0 => .label etc. - Fix URL generating in ClipboardController - Really check permission for excluded fields instead of returning always false - Handle translations in WidgetBuilder - Support arbitrary routes in `ViewHelpers::redirectHome` - Update referers in session for routes with attribute `_dcg_referer_update` (As Contao does for DC_Table) - Translate properties in panel elements --- .composer-require-checker.json | 2 +- composer.json | 8 +- src/BaseConfigRegistry.php | 10 +- .../Callback/AbstractCallbackListener.php | 9 +- ...tractReturningPropertyCallbackListener.php | 7 +- .../ContainerGlobalButtonCallbackListener.php | 9 +- .../ContainerOnLoadCallbackListener.php | 7 +- .../ModelOperationButtonCallbackListener.php | 9 +- src/Contao/Compatibility/DcCompat.php | 3 +- ...ExtendedLegacyDcaDataDefinitionBuilder.php | 22 +- .../Legacy/LegacyDcaDataDefinitionBuilder.php | 18 +- .../Populator/ParentDefinitionPopulator.php | 11 +- src/Contao/Event/Subscriber.php | 10 +- src/Contao/Picker/PagePickerProvider.php | 2 +- src/Contao/SessionStorage.php | 27 +- .../Subscriber/FormatModelLabelSubscriber.php | 6 +- .../AbstractListShowAllHandler.php | 91 +- .../ActionHandler/CopyHandler.php | 81 +- .../ActionHandler/CreateHandler.php | 6 +- .../MultipleHandler/EditAllHandler.php | 2 +- .../MultipleHandler/OverrideAllHandler.php | 2 +- .../MultipleHandler/PasteAllHandler.php | 24 +- .../SelectPropertyAllHandler.php | 26 +- .../ParentedListViewShowAllHandler.php | 31 +- .../ActionHandler/SelectHandler.php | 26 +- .../View/Contao2BackendView/BaseView.php | 6 +- .../Contao2BackendView/ButtonRenderer.php | 206 ++-- .../Controller/ClipboardController.php | 47 +- .../View/Contao2BackendView/EditMask.php | 2 +- .../ColorPickerWizardListener.php | 2 +- .../CreateModelButtonListener.php | 7 +- .../SelectModeButtonsListener.php | 2 +- .../View/Contao2BackendView/FileSelect.php | 2 +- .../GlobalButtonRenderer.php | 41 +- .../Subscriber/CheckPermission.php | 37 +- .../Subscriber/GetGroupHeaderSubscriber.php | 14 +- .../Subscriber/MultipleHandlerSubscriber.php | 7 +- .../Subscriber/WidgetBuilder.php | 26 +- .../View/Contao2BackendView/TreePicker.php | 6 +- .../View/Contao2BackendView/TreeView.php | 18 +- .../View/Contao2BackendView/ViewHelpers.php | 77 +- .../Contao2BackendView/Widget/PageTree.php | 6 +- src/Controller/Ajax.php | 3 + src/Controller/Ajax3X.php | 46 +- src/Controller/DefaultController.php | 12 +- src/Controller/SortingManager.php | 7 +- src/Controller/TreeCollector.php | 43 +- src/Data/DefaultDataProvider.php | 6 +- .../Definition/View/BackCommand.php | 2 +- .../Definition/View/CreateModelCommand.php | 9 +- .../Palette/Builder/PaletteBuilder.php | 18 +- .../Palette/PropertyTrueCondition.php | 6 +- src/DataDefinition/Palette/Palette.php | 4 +- .../Palette/PaletteInterface.php | 8 +- src/EventListener/StoreRefererListener.php | 142 +++ .../config/backend_event_subscribers.yml | 1 + .../contao/handler_backend_listeners.yml | 2 + src/Resources/config/event_listeners.yml | 7 + .../templates/dcbe_general_clipboard.html5 | 2 +- .../templates/dcbe_general_common_list.html5 | 18 +- .../contao/templates/dcbe_general_show.html5 | 2 +- src/Resources/public/css/generalDriver.css | 2 +- .../public/css/generalDriver.css.map | 2 +- src/Resources/public/js/vanillaGeneral.js | 2 +- src/Resources/public/sass/generalDriver.scss | 4 +- src/Resources/translations/dc-general.de.xlf | 924 +++++++++--------- src/Resources/translations/dc-general.en.xlf | 767 ++++++++------- .../AbstractPropertyVisibilityHandler.php | 29 +- .../Subscriber/CheckPermissionTest.php | 34 +- 69 files changed, 1771 insertions(+), 1286 deletions(-) create mode 100644 src/EventListener/StoreRefererListener.php diff --git a/.composer-require-checker.json b/.composer-require-checker.json index 0fb1f54b..4cdfb2db 100644 --- a/.composer-require-checker.json +++ b/.composer-require-checker.json @@ -1,6 +1,6 @@ { "symbol-whitelist": [ - "array", "bool", "false", "int", "mixed", "null", "self", "static", "parent", "string", "true", "void", + "array", "bool", "false", "int", "mixed", "never", "null", "self", "static", "parent", "string", "true", "void", "ContaoCommunityAlliance\\Contao\\EventDispatcher\\CcaEventDispatcherBundle", "ContaoCommunityAlliance\\DcGeneral\\Contao\\View\\Contao2BackendView\\Exception\\EditOnlyModeException", "ContaoCommunityAlliance\\DcGeneral\\Contao\\View\\Contao2BackendView\\Exception\\NotCreatableException", diff --git a/composer.json b/composer.json index c7e4fe33..b65497bc 100644 --- a/composer.json +++ b/composer.json @@ -36,7 +36,7 @@ "ext-json": "*", "ext-pdo": "*", "contao-community-alliance/events-contao-bindings": "^4.13", - "contao-community-alliance/translator": "^2.3", + "contao-community-alliance/translator": "^2.4.2", "contao-community-alliance/url-builder": "^1.3", "contao/core-bundle": "^4.13", "contao/image": "^1.1", @@ -95,12 +95,8 @@ }, "extra": { "branch-alias": { - "dev-master": "2.3.x-dev", - "dev-support/2.1.x": "2.1.x-dev" + "dev-master": "2.4.x-dev" }, "contao-manager-plugin": "ContaoCommunityAlliance\\DcGeneral\\ContaoManager\\Plugin" - }, - "scripts": { - "php-cs-fixer": "php-cs-fixer fix --rules=@PSR2" } } diff --git a/src/BaseConfigRegistry.php b/src/BaseConfigRegistry.php index 2ba062da..42e742f5 100644 --- a/src/BaseConfigRegistry.php +++ b/src/BaseConfigRegistry.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2024 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-2024 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -121,7 +122,7 @@ private function addParentFilter(ModelIdInterface $idParent, ConfigInterface $co $baseFilter = $config->getFilter(); $filter = $condition->getFilter($parent); - if ($baseFilter) { + if (\is_array($baseFilter)) { $filter = array_merge($baseFilter, $filter); } @@ -144,6 +145,7 @@ private function addParentFilter(ModelIdInterface $idParent, ConfigInterface $co private function buildBaseConfig(?ModelIdInterface $parentId): ConfigInterface { $environment = $this->getEnvironment(); + assert($environment instanceof EnvironmentInterface); $provider = $environment->getDataProvider(); if (null === $provider) { throw new DcGeneralRuntimeException('Data provider not set.'); @@ -156,7 +158,7 @@ private function buildBaseConfig(?ModelIdInterface $parentId): ConfigInterface $additional = $definition->getBasicDefinition()->getAdditionalFilter(); // Custom filter common for all modes. - if ($additional) { + if (\is_array($additional)) { $config->setFilter($additional); } diff --git a/src/Contao/Callback/AbstractCallbackListener.php b/src/Contao/Callback/AbstractCallbackListener.php index bf48c9f2..53390713 100644 --- a/src/Contao/Callback/AbstractCallbackListener.php +++ b/src/Contao/Callback/AbstractCallbackListener.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2022 Contao Community Alliance. + * (c) 2013-2024 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-2024 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -60,7 +60,7 @@ public function __construct($callback, $restrictions = null) { $this->callback = $callback; - if ($restrictions) { + if (\is_array($restrictions)) { call_user_func_array([$this, 'setRestrictions'], $restrictions); } } @@ -86,7 +86,7 @@ public function setRestrictions(?string $dataContainerName = null) */ public function wantToExecute($event) { - if (empty($this->dataContainerName)) { + if (null === $this->dataContainerName) { return true; } if (!$event instanceof EnvironmentAwareInterface) { @@ -95,6 +95,7 @@ public function wantToExecute($event) if (null === $definition = $event->getEnvironment()->getDataDefinition()) { return false; } + return ($this->dataContainerName === $definition->getName()); } diff --git a/src/Contao/Callback/AbstractReturningPropertyCallbackListener.php b/src/Contao/Callback/AbstractReturningPropertyCallbackListener.php index 08e1e5d8..8f680b38 100644 --- a/src/Contao/Callback/AbstractReturningPropertyCallbackListener.php +++ b/src/Contao/Callback/AbstractReturningPropertyCallbackListener.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2020 Contao Community Alliance. + * (c) 2013-2024 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,8 @@ * @package contao-community-alliance/dc-general * @author Christian Schiffler * @author Sven Baumann - * @copyright 2013-2020 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2024 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -66,7 +67,7 @@ public function setRestrictions(?string $dataContainerName = null, ?string $prop public function wantToExecute($event) { return parent::wantToExecute($event) - && (empty($this->propertyName) || ($this->propertyName === $this->getProperty($event))); + && (null === $this->propertyName || ($this->propertyName === $this->getProperty($event))); } private function getProperty(Event $event): string diff --git a/src/Contao/Callback/ContainerGlobalButtonCallbackListener.php b/src/Contao/Callback/ContainerGlobalButtonCallbackListener.php index b8d4063b..79638a1b 100644 --- a/src/Contao/Callback/ContainerGlobalButtonCallbackListener.php +++ b/src/Contao/Callback/ContainerGlobalButtonCallbackListener.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2024 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-2024 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -59,9 +60,7 @@ public function setRestrictions($dataContainerName = null, $operationName = null public function wantToExecute($event) { return parent::wantToExecute($event) - && (empty($this->operationName) - || ($this->operationName === $event->getKey()) - ); + && (null === $this->operationName || ($this->operationName === $event->getKey())); } /** diff --git a/src/Contao/Callback/ContainerOnLoadCallbackListener.php b/src/Contao/Callback/ContainerOnLoadCallbackListener.php index 635cfd5c..a0c8d505 100644 --- a/src/Contao/Callback/ContainerOnLoadCallbackListener.php +++ b/src/Contao/Callback/ContainerOnLoadCallbackListener.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2024 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-2024 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 ContainerOnLoadCallbackListener extends AbstractCallbackListener */ public function wantToExecute($event) { - if (empty($this->dataContainerName)) { + if (null === $this->dataContainerName) { return true; } if (null === $definition = $event->getDcGeneral()->getEnvironment()->getDataDefinition()) { diff --git a/src/Contao/Callback/ModelOperationButtonCallbackListener.php b/src/Contao/Callback/ModelOperationButtonCallbackListener.php index fc7fb2ff..f43536fe 100644 --- a/src/Contao/Callback/ModelOperationButtonCallbackListener.php +++ b/src/Contao/Callback/ModelOperationButtonCallbackListener.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2020 Contao Community Alliance. + * (c) 2013-2024 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-2020 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2024 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -69,9 +70,7 @@ public function wantToExecute($event) } return parent::wantToExecute($event) - && (empty($this->operationName) - || ($this->operationName === $event->getKey()) - ); + && (null === $this->operationName || ($this->operationName === $event->getKey())); } /** diff --git a/src/Contao/Compatibility/DcCompat.php b/src/Contao/Compatibility/DcCompat.php index f4a9e883..fcf820da 100644 --- a/src/Contao/Compatibility/DcCompat.php +++ b/src/Contao/Compatibility/DcCompat.php @@ -206,9 +206,10 @@ public function __get($name) return $dataProvider->getEmptyModel()->getProviderName(); case 'value': - if ($this->propertyName && $this->getModel()) { + if (null !== $this->propertyName && null !== $this->getModel()) { $model = $this->getModel(); assert($model instanceof ModelInterface); + return $model->getProperty($this->propertyName); } return null; diff --git a/src/Contao/Dca/Builder/Legacy/ExtendedLegacyDcaDataDefinitionBuilder.php b/src/Contao/Dca/Builder/Legacy/ExtendedLegacyDcaDataDefinitionBuilder.php index 17f1655b..e1736f64 100644 --- a/src/Contao/Dca/Builder/Legacy/ExtendedLegacyDcaDataDefinitionBuilder.php +++ b/src/Contao/Dca/Builder/Legacy/ExtendedLegacyDcaDataDefinitionBuilder.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2022 Contao Community Alliance. + * (c) 2013-2024 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 Tristan Lins * @author Sven Baumann * @author Ingolf Steinhardt - * @copyright 2013-2022 Contao Community Alliance. + * @copyright 2013-2024 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -118,7 +118,7 @@ function (PopulateEnvironmentEvent $event) use ($dataContainerName) { } $parentName = $definition->getBasicDefinition()->getParentDataProvider(); - if ($parentName) { + if (null !== $parentName) { $parentDefinition = ($parentName === $definition->getName()) ? $definition : (new DcGeneralFactory()) @@ -133,7 +133,7 @@ function (PopulateEnvironmentEvent $event) use ($dataContainerName) { } $rootName = $definition->getBasicDefinition()->getRootDataProvider(); - if ($rootName) { + if (null !== $rootName) { $rootDefinition = ($rootName === $definition->getName()) ? $definition : (new DcGeneralFactory()) @@ -221,13 +221,13 @@ protected function parseSingleDataProvider( ContainerInterface $container, DataProviderDefinitionInterface $providers, array $information, - $name + ?string $name ) { if (isset($information['factory'])) { $providerInformation = (new \ReflectionClass($information['factory']))->newInstance()->build($information); } else { // Determine the name. - if ($name && !$this->isSpecialName($name)) { + if (null !== $name && !$this->isSpecialName($name)) { $providerName = $name; } elseif ('default' === $name) { $providerName = $container->getName(); @@ -249,13 +249,11 @@ protected function parseSingleDataProvider( if (!$providerInformation instanceof ContaoDataProviderInformation) { return $providerInformation; } - if (!$providerInformation->getTableName()) { + if (null === $providerInformation->getTableName()) { if (isset($information['source'])) { - $providerInformation - ->setTableName($information['source']); + $providerInformation->setTableName($information['source']); } else { - $providerInformation - ->setTableName($providerName); + $providerInformation->setTableName($providerName); } } @@ -384,7 +382,7 @@ protected function parseRootCondition( if (null !== ($rootCondition = $this->getFromDca('dca_config/rootEntries'))) { $rootProvider = $container->getBasicDefinition()->getRootDataProvider(); - if (!$rootProvider) { + if (null === $rootProvider) { throw new DcGeneralRuntimeException( 'Root data provider name not specified in DCA but rootEntries section specified.' ); diff --git a/src/Contao/Dca/Builder/Legacy/LegacyDcaDataDefinitionBuilder.php b/src/Contao/Dca/Builder/Legacy/LegacyDcaDataDefinitionBuilder.php index 36707e75..cf55c956 100644 --- a/src/Contao/Dca/Builder/Legacy/LegacyDcaDataDefinitionBuilder.php +++ b/src/Contao/Dca/Builder/Legacy/LegacyDcaDataDefinitionBuilder.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2023 Contao Community Alliance. + * (c) 2013-2024 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 Stefan Heimes * @author Sven Baumann * @author Ingolf Steinhardt - * @copyright 2013-2023 Contao Community Alliance. + * @copyright 2013-2024 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -120,7 +120,6 @@ use function explode; use function in_array; use function is_array; -use function is_callable; use function next; use function parse_str; use function reset; @@ -515,7 +514,7 @@ protected function parseDataProvider(ContainerInterface $container) // If mode is 5, we need to define tree view. if ( (5 === $this->getFromDca('list/sorting/mode')) - && !$container->getBasicDefinition()->getRootDataProvider() + && null === $container->getBasicDefinition()->getRootDataProvider() ) { $container->getBasicDefinition()->setRootDataProvider($container->getName()); } @@ -545,16 +544,16 @@ protected function parseDataProvider(ContainerInterface $container) ) ); - if (!$container->getBasicDefinition()->getRootDataProvider()) { + if (null === $container->getBasicDefinition()->getRootDataProvider()) { $container->getBasicDefinition()->setRootDataProvider($parentTable); } - if (!$container->getBasicDefinition()->getParentDataProvider()) { + if (null === $container->getBasicDefinition()->getParentDataProvider()) { $container->getBasicDefinition()->setParentDataProvider($parentTable); } } } - $providerName = $container->getBasicDefinition()->getDataProvider() ?: $container->getName(); + $providerName = $container->getBasicDefinition()->getDataProvider() ?? $container->getName(); // Check config if it already exists, if not, add it. if (!$config->hasInformation($providerName)) { @@ -591,7 +590,7 @@ protected function parseDataProvider(ContainerInterface $container) // @codingStandardsIgnoreEnd } - if (!$container->getBasicDefinition()->getDataProvider()) { + if (null === $container->getBasicDefinition()->getDataProvider()) { $container->getBasicDefinition()->setDataProvider($providerName); } } @@ -626,7 +625,7 @@ protected function getRootProviderName(ContainerInterface $container) { $rootProvider = $container->getBasicDefinition()->getRootDataProvider(); - if (!$rootProvider) { + if (null === $rootProvider) { throw new DcGeneralRuntimeException( 'Root data provider name not specified in DCA but rootEntries section specified.' ); @@ -873,6 +872,7 @@ protected function parseListSorting(ListingConfigInterface $listing, array $list $parsedProperties = []; $sortingDca = ($listDca['sorting'] ?? []); + /** @psalm-suppress RiskyTruthyFalsyComparison */ if ($headerFields = ($sortingDca['headerFields'] ?? [])) { assert(\is_array($headerFields)); /** @var list $headerFields */ diff --git a/src/Contao/Dca/Populator/ParentDefinitionPopulator.php b/src/Contao/Dca/Populator/ParentDefinitionPopulator.php index ffcc2eac..c51866f1 100644 --- a/src/Contao/Dca/Populator/ParentDefinitionPopulator.php +++ b/src/Contao/Dca/Populator/ParentDefinitionPopulator.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2024 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-2024 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -47,9 +48,13 @@ public function populateController(EnvironmentInterface $environment) { $definition = $environment->getDataDefinition(); - if (!$definition || !($parentDataProvider = $definition->getBasicDefinition()->getParentDataProvider())) { + if ( + null === $definition + || null === ($parentDataProvider = $definition->getBasicDefinition()->getParentDataProvider()) + ) { return; } + if (null === $dispatcher = $environment->getEventDispatcher()) { throw new LogicException('No event dispatcher given'); } diff --git a/src/Contao/Event/Subscriber.php b/src/Contao/Event/Subscriber.php index ca9ea3cf..8389ac93 100644 --- a/src/Contao/Event/Subscriber.php +++ b/src/Contao/Event/Subscriber.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2023 Contao Community Alliance. + * (c) 2013-2024 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 David Molineus * @author Stefan Heimes * @author Ingolf Steinhardt - * @copyright 2013-2023 Contao Community Alliance. + * @copyright 2013-2024 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -514,7 +514,7 @@ private static function renderSimpleCheckbox( ): void { if ( (null !== $event->getRendered()) - || !(!($extra['multiple'] ?? false) && ('checkbox' === $property->getWidgetType())) + || !(!((bool) ($extra['multiple'] ?? false)) && ('checkbox' === $property->getWidgetType())) ) { return; } @@ -625,9 +625,9 @@ private static function renderOptionValueReadable( return; } - if (!($options = $property->getOptions())) { + if (null === ($options = $property->getOptions())) { $options = self::getOptions($event->getEnvironment(), $event->getModel(), $event->getProperty()); - if ($options) { + if (null !== $options) { $property->setOptions($options); } } diff --git a/src/Contao/Picker/PagePickerProvider.php b/src/Contao/Picker/PagePickerProvider.php index 35a7563e..fce99844 100644 --- a/src/Contao/Picker/PagePickerProvider.php +++ b/src/Contao/Picker/PagePickerProvider.php @@ -67,7 +67,7 @@ public function __construct( */ public function getName() { - return 'ccaPagePicker'; + return 'pagePicker'; } /** diff --git a/src/Contao/SessionStorage.php b/src/Contao/SessionStorage.php index b6d7956a..4c7c9630 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-2023 Contao Community Alliance. + * (c) 2013-2024 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-2023 Contao Community Alliance. + * @copyright 2013-2024 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -43,21 +43,21 @@ class SessionStorage implements SessionStorageInterface * * @var SessionInterface */ - private $session; + private SessionInterface $session; /** * The database keys for store session data in the database. * * @var array */ - private $databaseKeys = []; + private array $databaseKeys = []; /** * The attribute storage. * * @var array */ - private $attributes = []; + private array $attributes = []; /** * Create a new instance. @@ -77,7 +77,7 @@ public function __construct( foreach ($databaseKeys as $index => $databaseKeyItems) { foreach ((array) $databaseKeyItems as $databaseKey) { - if (('common' === $index) || (0 === \strpos($index, 'DC_GENERAL_'))) { + if (('common' === $index) || (\str_starts_with($index, 'DC_GENERAL_'))) { $this->databaseKeys[$index][] = $databaseKey; continue; @@ -97,7 +97,7 @@ public function __construct( */ public function setScope($scope) { - if ($this->scope) { + if (null !== $this->scope) { // @codingStandardsIgnoreStart @\trigger_error('The scope can not be change! Use a new session storage.', E_USER_ERROR); // @codingStandardsIgnoreEnd @@ -206,7 +206,7 @@ private function load() * * @return void */ - private function persist() + private function persist(): void { if (null === $scope = $this->getScope()) { return; @@ -229,10 +229,10 @@ private function persist() * * @return array */ - private function filterAttributes($determineDatabase = false) + private function filterAttributes(bool $determineDatabase = false): array { $databaseAttributes = $this->databaseKeys['common'] ?? []; - if ($scope = $this->getScope()) { + if (null !== ($scope = $this->getScope())) { $databaseAttributes = \array_merge($databaseAttributes, $this->databaseKeys[$scope] ?? []); } @@ -250,10 +250,11 @@ private function filterAttributes($determineDatabase = false) */ private function getScope(): ?string { - if (!$this->scope) { + if (null === $this->scope) { // @codingStandardsIgnoreStart @\trigger_error('The scope for this session storage is not defined!', E_USER_ERROR); // @codingStandardsIgnoreEnd + return null; } @@ -265,7 +266,7 @@ private function getScope(): ?string * * @return string */ - private function getSessionBagKey() + private function getSessionBagKey(): string { return 'cca_dc_general'; } @@ -275,7 +276,7 @@ private function getSessionBagKey() * * @return string */ - private function getDatabaseSessionBagKey() + private function getDatabaseSessionBagKey(): string { return 'contao_backend'; } diff --git a/src/Contao/Subscriber/FormatModelLabelSubscriber.php b/src/Contao/Subscriber/FormatModelLabelSubscriber.php index 74d42a0a..10fc1a42 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-2023 Contao Community Alliance. + * (c) 2013-2024 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 Sven Baumann * @author Ingolf Steinhardt * @author Cliff Parnitzky - * @copyright 2013-2023 Contao Community Alliance. + * @copyright 2013-2024 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -191,7 +191,7 @@ private function renderWithColumns(array $propertyNames, array|string $args, str $label[] = [ 'colspan' => 1, 'class' => $class, - 'content' => ($args[$propertyName] ?? null) ?: '-' + 'content' => ($args[$propertyName] ?? '') ?: '-' ]; } } diff --git a/src/Contao/View/Contao2BackendView/ActionHandler/AbstractListShowAllHandler.php b/src/Contao/View/Contao2BackendView/ActionHandler/AbstractListShowAllHandler.php index b1519198..bbb8bf39 100644 --- a/src/Contao/View/Contao2BackendView/ActionHandler/AbstractListShowAllHandler.php +++ b/src/Contao/View/Contao2BackendView/ActionHandler/AbstractListShowAllHandler.php @@ -76,6 +76,9 @@ use function array_key_exists; use function implode; +use function in_array; +use function is_string; +use function sprintf; use function str_contains; use function str_replace; use function trigger_error; @@ -154,7 +157,7 @@ public function __construct( } if (null === $tokenName) { $tokenName = System::getContainer()->getParameter('contao.csrf_token_name'); - assert(\is_string($tokenName)); + assert(is_string($tokenName)); // @codingStandardsIgnoreStart @trigger_error( @@ -230,12 +233,12 @@ protected function process(Action $action, EnvironmentInterface $environment) $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('mode', ($grouping['mode'] ?? null)) ->set('theme', Backend::getTheme()); $this->renderTemplate($template, $environment); @@ -264,7 +267,7 @@ protected function process(Action $action, EnvironmentInterface $environment) * * @return string */ - private function languageSwitcher(EnvironmentInterface $environment) + private function languageSwitcher(EnvironmentInterface $environment): string { $template = new ContaoBackendViewTemplate('dcbe_general_language_selector'); @@ -333,40 +336,47 @@ protected function translate($key, $domain, array $parameters = []) ); // @codingStandardsIgnoreEnd + $oldKey = sprintf('%s.%s', $domain, $key); $translated = $this->translator->trans( - \sprintf('%s.%s', $domain, $key), + $oldKey, $parameters, - \sprintf('contao_%s', $domain) + sprintf('contao_%s', $domain) ); + if ($translated === $oldKey) { + return $key; + } } return $translated; } - protected function translateButtonLabel(string $buttonName, $definitionName): string + protected function translateButtonLabel(string $buttonName, string $definitionName, array $parameter = []): string { // New way via symfony translator. - if ($buttonName . '.label' !== ($header = $this->translate($buttonName . '.label', $definitionName))) { + if ( + $buttonName . '.label' !== ($header = $this->translate($buttonName . '.label', $definitionName, $parameter)) + ) { return $header; } - // FIXME: Fallback to legacy translator. - return $this->translate($buttonName . '.0', $definitionName); + return $this->translate($buttonName . '.0', $definitionName, $parameter); } - protected function translateButtonDescription(string $buttonName, $definitionName): string - { + protected function translateButtonDescription( + string $buttonName, + string $definitionName, + array $parameter = [] + ): string { // New way via symfony translator. if ( $buttonName . '.description' - !== ($header = $this->translate($buttonName . '.description', $definitionName)) + !== ($header = $this->translate($buttonName . '.description', $definitionName, $parameter)) ) { return $header; } - // FIXME: Fallback to legacy translator. - return $this->translate($buttonName . '.1', $definitionName); + return $this->translate($buttonName . '.1', $definitionName, $parameter); } /** @@ -491,7 +501,7 @@ protected function loadCollection(EnvironmentInterface $environment) * * @return string */ - private function generateHeaderButtons(EnvironmentInterface $environment) + private function generateHeaderButtons(EnvironmentInterface $environment): string { return (new GlobalButtonRenderer($environment))->render(); } @@ -509,7 +519,7 @@ private function renderCollection( EnvironmentInterface $environment, CollectionInterface $collection, array $grouping - ) { + ): void { $definition = $environment->getDataDefinition(); assert($definition instanceof ContainerInterface); @@ -533,7 +543,7 @@ private function renderCollection( /** @var ModelInterface $model */ $this->addGroupHeader($environment, $grouping, $model, $groupClass, $eoCount, $remoteCur); - if ($listing->getItemCssClass()) { + if (null !== $listing->getItemCssClass()) { $model->setMeta($model::CSS_CLASS, $listing->getItemCssClass()); } $cssClasses = [(0 === (++$eoCount) % 2) ? 'even' : 'odd']; @@ -575,7 +585,7 @@ private function addGroupHeader( &$groupClass, &$eoCount, &$remoteCur = null - ) { + ): void { if ($grouping && GroupAndSortingInformationInterface::GROUP_NONE !== $grouping['mode']) { $remoteNew = $this->renderGroupHeader( $grouping['property'], @@ -609,7 +619,7 @@ private function addGroupHeader( * * @return string When no information of panels can be obtained from the data container. */ - private function panel(EnvironmentInterface $environment, $ignoredPanels = []) + private function panel(EnvironmentInterface $environment, array $ignoredPanels = []): string { $view = $environment->getView(); assert($view instanceof BackendViewInterface); @@ -624,22 +634,19 @@ private function panel(EnvironmentInterface $environment, $ignoredPanels = []) * * @return array */ - private function getTableHead(EnvironmentInterface $environment) + private function getTableHead(EnvironmentInterface $environment): array { $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' : ''), - 'content' => $properties->hasProperty($field) - ? $properties->getProperty($field)->getLabel() - : $this->translate($definition->getName() . '.' . $field . '.0', 'contao_' . $definition->getName()) + 'class' => 'tl_folder_tlist col_' . $field . (in_array($field, $columns) ? ' ordered_by' : ''), + 'content' => $this->translateButtonLabel($field, $definition->getName()) ]; } @@ -666,12 +673,12 @@ private function getTableHead(EnvironmentInterface $environment) * @SuppressWarnings(PHPMD.CamelCaseVariableName) */ private function renderGroupHeader( - $field, + string $field, ModelInterface $model, - $groupMode, - $groupLength, + string $groupMode, + int $groupLength, EnvironmentInterface $environment - ) { + ): ?string { $definition = $environment->getDataDefinition(); assert($definition instanceof ContainerInterface); @@ -716,7 +723,7 @@ protected function getSelectButtons(EnvironmentInterface $environment) * * @return bool */ - private function isSortable(EnvironmentInterface $environment) + private function isSortable(EnvironmentInterface $environment): bool { $definition = $environment->getDataDefinition(); assert($definition instanceof ContainerInterface); @@ -744,8 +751,6 @@ protected function renderPasteTopButton(EnvironmentInterface $environment, $sort $dispatcher = $environment->getEventDispatcher(); assert($dispatcher instanceof EventDispatcherInterface); - $languageDomain = 'contao_' . $definition->getName(); - $filter = new Filter(); assert($filter instanceof FilterInterface); @@ -753,7 +758,7 @@ protected function renderPasteTopButton(EnvironmentInterface $environment, $sort assert($basicDefinition instanceof BasicDefinitionInterface); $dataProvider = $basicDefinition->getDataProvider(); - assert(\is_string($dataProvider)); + assert(is_string($dataProvider)); $filter->andModelIsFromProvider($dataProvider); @@ -764,7 +769,7 @@ protected function renderPasteTopButton(EnvironmentInterface $environment, $sort return ''; } - if (!ViewHelpers::getManualSortingProperty($environment)) { + if (null === ViewHelpers::getManualSortingProperty($environment)) { return ''; } @@ -780,16 +785,16 @@ protected function renderPasteTopButton(EnvironmentInterface $environment, $sort $imageEvent = $dispatcher->dispatch( new GenerateHtmlEvent( 'pasteafter.svg', - $this->translate('pasteafter.0', $languageDomain), + $this->translateButtonLabel('pastenew', $definition->getName()), 'class="blink"' ), ContaoEvents::IMAGE_GET_HTML ); - return \sprintf( + return sprintf( '%s', $urlEvent->getUrl(), - StringUtil::specialchars($this->translate('pasteafter.0', $languageDomain)), + $this->translateButtonLabel('pastenew', $definition->getName()), $imageEvent->getHtml() ?? '' ); } @@ -804,7 +809,7 @@ protected function renderPasteTopButton(EnvironmentInterface $environment, $sort * @SuppressWarnings(PHPMD.Superglobals) * @SuppressWarnings(PHPMD.CamelCaseVariableName) */ - private function breadcrumb(EnvironmentInterface $environment) + private function breadcrumb(EnvironmentInterface $environment): ?string { $event = new GetBreadcrumbEvent($environment); @@ -832,7 +837,7 @@ private function breadcrumb(EnvironmentInterface $environment) * * @return array */ - private function getSortingColumns($sortingDefinition) + private function getSortingColumns(?GroupAndSortingDefinitionInterface $sortingDefinition): array { if (null === $sortingDefinition) { return []; @@ -857,7 +862,7 @@ private function getSortingColumns($sortingDefinition) * * @return array */ - private function getSelectContainer(EnvironmentInterface $environment) + private function getSelectContainer(EnvironmentInterface $environment): array { $inputProvider = $environment->getInputProvider(); assert($inputProvider instanceof InputProviderInterface); @@ -894,7 +899,7 @@ private function getSelectContainer(EnvironmentInterface $environment) * * @return void */ - private function handleEditAllButton(CollectionInterface $collection, EnvironmentInterface $environment) + private function handleEditAllButton(CollectionInterface $collection, EnvironmentInterface $environment): void { if (0 < $collection->count()) { return; diff --git a/src/Contao/View/Contao2BackendView/ActionHandler/CopyHandler.php b/src/Contao/View/Contao2BackendView/ActionHandler/CopyHandler.php index 754ef9d8..4d69756c 100644 --- a/src/Contao/View/Contao2BackendView/ActionHandler/CopyHandler.php +++ b/src/Contao/View/Contao2BackendView/ActionHandler/CopyHandler.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2023 Contao Community Alliance. + * (c) 2013-2024 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -17,14 +17,13 @@ * @author Sven Baumann * @author Richard Henkenjohann * @author Ingolf Steinhardt - * @copyright 2013-2023 Contao Community Alliance. + * @copyright 2013-2024 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\ActionHandler; -use Contao\System; use ContaoCommunityAlliance\Contao\Bindings\ContaoEvents; use ContaoCommunityAlliance\Contao\Bindings\Events\Controller\RedirectEvent; use ContaoCommunityAlliance\Contao\Bindings\Events\System\LogEvent; @@ -48,6 +47,8 @@ use ContaoCommunityAlliance\UrlBuilder\Contao\CsrfUrlBuilderFactory; use ContaoCommunityAlliance\UrlBuilder\UrlBuilder; use Symfony\Component\EventDispatcher\EventDispatcherInterface; +use Symfony\Component\HttpFoundation\RequestStack; +use Symfony\Component\Routing\Generator\UrlGeneratorInterface; /** * Class CopyModelController handles copy action on a model. @@ -65,19 +66,41 @@ class CopyHandler * * @var CsrfUrlBuilderFactory */ - private $securityUrlBuilder; + private CsrfUrlBuilderFactory $securityUrlBuilder; + + /** + * The request stack. + * + * @var RequestStack + */ + private RequestStack $requestStack; + + /** + * The URL generator. + * + * @var UrlGeneratorInterface + */ + private UrlGeneratorInterface $urlGenerator; /** * PasteHandler constructor. * * @param RequestScopeDeterminator $scopeDeterminator The request mode determinator. * @param CsrfUrlBuilderFactory $securityUrlBuilder The URL builder factory for URLs with security token. + * @param RequestStack $requestStack The request stack. + * @param UrlGeneratorInterface $urlGenerator The URL generator. */ - public function __construct(RequestScopeDeterminator $scopeDeterminator, CsrfUrlBuilderFactory $securityUrlBuilder) - { + public function __construct( + RequestScopeDeterminator $scopeDeterminator, + CsrfUrlBuilderFactory $securityUrlBuilder, + RequestStack $requestStack, + UrlGeneratorInterface $urlGenerator, + ) { $this->setScopeDeterminator($scopeDeterminator); $this->securityUrlBuilder = $securityUrlBuilder; + $this->requestStack = $requestStack; + $this->urlGenerator = $urlGenerator; } /** @@ -230,24 +253,38 @@ protected function redirect($environment, $copiedModelId) return; } - // Build a clean url to remove the copy related arguments instead of using the AddToUrlEvent. - $urlBuilder = new UrlBuilder(); - $urlBuilder - ->setPath('contao') - ->setQueryParameter('do', $inputProvider->getParameter('do')) - ->setQueryParameter('table', $copiedModelId->getDataProviderName()) - ->setQueryParameter('act', 'edit') - ->setQueryParameter('id', $copiedModelId->getSerialized()); - if (null !== ($pid = $inputProvider->getParameter('pid'))) { - $urlBuilder->setQueryParameter('pid', $pid); - } - - $redirectEvent = new RedirectEvent($this->securityUrlBuilder->create($urlBuilder->getUrl())->getUrl()); - if (null === ($dispatcher = $environment->getEventDispatcher())) { return; } + $request = $this->requestStack->getCurrentRequest(); + $routeName = $request?->attributes->get('_route'); + // Build a clean url to remove the copy related arguments instead of using the AddToUrlEvent. + $urlBuilder = new UrlBuilder(); + if ($routeName !== 'contao.backend') { + $params = [ + 'table' => $copiedModelId->getDataProviderName(), + 'act' => 'edit', + 'id' => $copiedModelId->getSerialized(), + ]; + if (null !== ($pid = $inputProvider->getParameter('pid'))) { + $params['pid'] = $pid; + } + $url = $this->urlGenerator->generate($routeName, $params); + } else { + $urlBuilder + ->setPath('contao') + ->setQueryParameter('do', $inputProvider->getParameter('do')) + ->setQueryParameter('table', $copiedModelId->getDataProviderName()) + ->setQueryParameter('act', 'edit') + ->setQueryParameter('id', $copiedModelId->getSerialized()); + if (null !== ($pid = $inputProvider->getParameter('pid'))) { + $urlBuilder->setQueryParameter('pid', $pid); + } + $url = $urlBuilder->getUrl(); + } + $redirectEvent = new RedirectEvent($this->securityUrlBuilder->create($url)->getUrl()); + $dispatcher->dispatch($redirectEvent, ContaoEvents::CONTROLLER_REDIRECT); } @@ -285,7 +322,7 @@ protected function process(EnvironmentInterface $environment) return false; } - if ($manualSorting && $provider->fieldExists($manualSorting)) { + if (null !== $manualSorting && $provider->fieldExists($manualSorting)) { return false; } @@ -308,7 +345,7 @@ protected function process(EnvironmentInterface $environment) * * @return string|bool */ - private function checkPermission(EnvironmentInterface $environment) + private function checkPermission(EnvironmentInterface $environment): bool|string { if (null === ($definition = $environment->getDataDefinition())) { return false; diff --git a/src/Contao/View/Contao2BackendView/ActionHandler/CreateHandler.php b/src/Contao/View/Contao2BackendView/ActionHandler/CreateHandler.php index fcbef4e8..cd7d3a87 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-2023 Contao Community Alliance. + * (c) 2013-2024 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-2023 Contao Community Alliance. + * @copyright 2013-2024 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -90,7 +90,7 @@ public function handleEvent(ActionEvent $event) // Only handle if we do not have a manual sorting, or we know where to insert. // Manual sorting is handled by clipboard. if ( - ViewHelpers::getManualSortingProperty($environment) + null !== ViewHelpers::getManualSortingProperty($environment) && !$inputProvider->hasParameter('after') && !$inputProvider->hasParameter('into') ) { diff --git a/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/EditAllHandler.php b/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/EditAllHandler.php index 30844dd1..55167ba4 100644 --- a/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/EditAllHandler.php +++ b/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/EditAllHandler.php @@ -108,7 +108,7 @@ private function process(Action $action, EnvironmentInterface $environment): str [ 'subHeadline' => $translator->translate($inputProvider->getParameter('mode') . 'Selected', 'dc-general') . ': ' . - $translator->translate('all_label', 'dc-general'), + $translator->translate('editAll.label', 'dc-general'), 'fieldsets' => $renderInformation->offsetGet('fieldsets'), 'table' => $definition->getName(), 'error' => $renderInformation->offsetGet('error'), diff --git a/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/OverrideAllHandler.php b/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/OverrideAllHandler.php index 86edb89f..2224e88e 100644 --- a/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/OverrideAllHandler.php +++ b/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/OverrideAllHandler.php @@ -132,7 +132,7 @@ private function process(Action $action, EnvironmentInterface $environment) [ 'subHeadline' => $translator->translate($inputProvider->getParameter('mode') . 'Selected', 'dc-general') . ': ' . - $translator->translate('all_label', 'dc-general'), + $translator->translate('editAll.label', 'dc-general'), 'fieldsets' => $renderInformation->offsetGet('fieldsets'), 'table' => $definition->getName(), 'error' => $renderInformation->offsetGet('error'), diff --git a/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/PasteAllHandler.php b/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/PasteAllHandler.php index ef26626b..51a0919d 100644 --- a/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/PasteAllHandler.php +++ b/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/PasteAllHandler.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2023 Contao Community Alliance. + * (c) 2013-2024 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-2023 Contao Community Alliance. + * @copyright 2013-2024 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0 * @filesource */ @@ -58,14 +58,14 @@ class PasteAllHandler * * @var ModelInterface|null */ - protected $copiedModel = null; + protected ?ModelInterface $copiedModel = null; /** * The original model is available by paste mode copy. * * @var ModelInterface|null */ - protected $originalModel = null; + protected ?ModelInterface $originalModel = null; /** @@ -157,7 +157,7 @@ protected function getClipboardItems(EnvironmentInterface $environment) $filter = new Filter(); $filter->andModelIsFromProvider($provider); - if ($basicDefinition->getParentDataProvider()) { + if (null !== $basicDefinition->getParentDataProvider()) { $filter->andParentIsFromProvider($parentProvider); } else { $filter->andHasNoParent(); @@ -209,7 +209,9 @@ protected function getFlatCollection(EnvironmentInterface $environment) continue; } $pasteAfter = - $previousItem ? $previousItem->getModelId()->getSerialized() : $inputProvider->getParameter('after'); + null !== $previousItem + ? $previousItem->getModelId()->getSerialized() + : $inputProvider->getParameter('after'); $collection[$clipboardItem->getModelId()->getSerialized()] = [ 'item' => $clipboardItem, @@ -263,9 +265,11 @@ protected function getHierarchyCollection(array $clipboardItems, EnvironmentInte continue; } - $pasteMode = $previousItem ? 'after' : $originalPasteMode; + $pasteMode = null !== $previousItem ? 'after' : $originalPasteMode; $pasteAfter = - $previousItem ? $previousItem->getModelId()->getSerialized() : $inputProvider->getParameter($pasteMode); + null !== $previousItem + ? $previousItem->getModelId()->getSerialized() + : $inputProvider->getParameter($pasteMode); $collection[$modelId->getSerialized()] = [ 'item' => $clipboardItem, @@ -366,7 +370,9 @@ protected function setSubItemsToCollection( $modelId = $subClipboardItem->getModelId(); $pasteAfter = - $intoItem ? $intoItem->getModelId()->getSerialized() : $previousModelId->getSerialized(); + null !== $intoItem + ? $intoItem->getModelId()->getSerialized() + : $previousModelId->getSerialized(); $intoItem = $subClipboardItem; diff --git a/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/SelectPropertyAllHandler.php b/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/SelectPropertyAllHandler.php index 81c5a7c7..f55073eb 100644 --- a/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/SelectPropertyAllHandler.php +++ b/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/SelectPropertyAllHandler.php @@ -116,7 +116,7 @@ protected function loadCollection(EnvironmentInterface $environment) * * @throws DcGeneralRuntimeException When no source has been defined. */ - private function getPropertyDataProvider(EnvironmentInterface $environment) + private function getPropertyDataProvider(EnvironmentInterface $environment): NoOpDataProvider { $definition = $environment->getDataDefinition(); assert($definition instanceof ContainerInterface); @@ -139,7 +139,7 @@ private function getPropertyDataProvider(EnvironmentInterface $environment) * * @return void */ - private function setPropertyLabelFormatter($providerName, EnvironmentInterface $environment) + private function setPropertyLabelFormatter(string $providerName, EnvironmentInterface $environment): void { $definition = $environment->getDataDefinition(); assert($definition instanceof ContainerInterface); @@ -176,8 +176,10 @@ private function setPropertyLabelFormatter($providerName, EnvironmentInterface $ * * @return CollectionInterface */ - private function getCollection(DataProviderInterface $dataProvider, EnvironmentInterface $environment) - { + private function getCollection( + DataProviderInterface $dataProvider, + EnvironmentInterface $environment + ): CollectionInterface { $collection = $dataProvider->getEmptyCollection(); $definition = $environment->getDataDefinition(); @@ -192,11 +194,11 @@ private function getCollection(DataProviderInterface $dataProvider, EnvironmentI $model->setID($property->getName()); $model->setProperty( 'name', - $property->getLabel() ?: $property->getName() + $this->translator->trans($property->getLabel(), [], $definition->getName()) ); $model->setProperty( 'description', - $property->getDescription() ?: $property->getName() + $this->translator->trans($property->getDescription(), [], $definition->getName()) ); $this->handlePropertyFileTree($property); @@ -216,7 +218,7 @@ private function getCollection(DataProviderInterface $dataProvider, EnvironmentI * * @return bool */ - private function isPropertyAllowed(PropertyInterface $property, EnvironmentInterface $environment) + private function isPropertyAllowed(PropertyInterface $property, EnvironmentInterface $environment): bool { if (!$property->getWidgetType() || ('dummyProperty' === $property->getWidgetType())) { return false; @@ -261,7 +263,7 @@ private function isPropertyAllowed(PropertyInterface $property, EnvironmentInter * * @return bool */ - private function isPropertyAllowedByEdit(array $extra, EnvironmentInterface $environment) + private function isPropertyAllowedByEdit(array $extra, EnvironmentInterface $environment): bool { $inputProvider = $environment->getInputProvider(); assert($inputProvider instanceof InputProviderInterface); @@ -278,7 +280,7 @@ private function isPropertyAllowedByEdit(array $extra, EnvironmentInterface $env * * @return bool */ - private function isPropertyAllowedByOverride(array $extra, EnvironmentInterface $environment) + private function isPropertyAllowedByOverride(array $extra, EnvironmentInterface $environment): bool { $inputProvider = $environment->getInputProvider(); assert($inputProvider instanceof InputProviderInterface); @@ -300,7 +302,7 @@ private function isPropertyAllowedByOverride(array $extra, EnvironmentInterface private function isPropertyAllowedByIntersectProperties( PropertyInterface $property, EnvironmentInterface $environment - ) { + ): bool { $sessionStorage = $environment->getSessionStorage(); assert($sessionStorage instanceof SessionStorageInterface); @@ -324,7 +326,7 @@ private function isPropertyAllowedByIntersectProperties( * * @SuppressWarnings(PHPMD.Superglobals) */ - private function handlePropertyFileTree(PropertyInterface $property) + private function handlePropertyFileTree(PropertyInterface $property): void { if ('fileTree' !== $property->getWidgetType()) { return; @@ -361,7 +363,7 @@ private function handlePropertyFileTree(PropertyInterface $property) * * @return void */ - private function handlePropertyFileTreeOrder(PropertyInterface $property, ModelInterface $model) + private function handlePropertyFileTreeOrder(PropertyInterface $property, ModelInterface $model): void { if ('fileTreeOrder' !== $property->getWidgetType()) { return; diff --git a/src/Contao/View/Contao2BackendView/ActionHandler/ParentedListViewShowAllHandler.php b/src/Contao/View/Contao2BackendView/ActionHandler/ParentedListViewShowAllHandler.php index 4cd50ff4..7a77a866 100644 --- a/src/Contao/View/Contao2BackendView/ActionHandler/ParentedListViewShowAllHandler.php +++ b/src/Contao/View/Contao2BackendView/ActionHandler/ParentedListViewShowAllHandler.php @@ -239,8 +239,8 @@ private function translateHeaderColumnName($field, $parentName) // FIXME: deprecation here? - old translation handling. return ('tstamp' === $field) - ? $this->translate('tstamp', 'contao_dc-general') - : $this->translate(\sprintf('%s.0', $field), 'contao_' . $parentName); + ? $this->translate('tstamp', 'dc-general') + : $this->translate(\sprintf('%s.0', $field), $parentName); } /** @@ -254,6 +254,7 @@ private function translateHeaderColumnName($field, $parentName) */ private function renderParentProperty(EnvironmentInterface $environment, $property, $value) { + /** @var array{reference?: array, isAssociative?: bool} $evaluation */ $evaluation = $property->getExtra(); if (\is_array($value)) { @@ -403,7 +404,7 @@ private function getParentModelButtons($parentModel, EnvironmentInterface $envir ' ', [ 'editHeader' => $this->getHeaderEditButton($parentModel, $environment), - 'pasteNew' => $this->getHeaderPasteNewButton($parentModel, $environment), + 'pasteNew' => $this->getHeaderPasteNewButton($parentModel, $environment), 'pasteAfter' => $this->getHeaderPasteTopButton($parentModel, $environment) ] ); @@ -450,8 +451,9 @@ protected function getHeaderEditButton(ModelInterface $parentModel, EnvironmentI $parameters['table'] = $parentName; $parameters['pid'] = ''; + /** @var array{idparam?: string} $extra */ $extra = (array) $command->getExtra(); - if ($idParam = ($extra['idparam'] ?? null)) { + if (null !== ($idParam = ($extra['idparam'] ?? null))) { $parameters[$idParam] = ModelId::fromModel($parentModel)->getSerialized(); } else { $parameters['id'] = ModelId::fromModel($parentModel)->getSerialized(); @@ -484,9 +486,10 @@ protected function getHeaderEditButton(ModelInterface $parentModel, EnvironmentI '%s', $urlAfter->getUrl(), StringUtil::specialchars( - \sprintf( - $this->translateButtonDescription('editheader', $parentDefinition->getName()), - $parentModel->getId() + $this->translateButtonDescription( + 'editheader', + $parentDefinition->getName(), + ['%id%' => $parentModel->getId()] ) ), $imageEvent->getHtml() ?? '' @@ -518,7 +521,7 @@ private function getHeaderPasteNewButton(ModelInterface $parentModel, Environmen $filter = new Filter(); $filter->andModelIsFromProvider($dataProvider); - if ($parentProviderName = $basicDefinition->getParentDataProvider()) { + if (null !== ($parentProviderName = $basicDefinition->getParentDataProvider())) { $filter->andParentIsFromProvider($parentProviderName); } else { $filter->andHasNoParent(); @@ -547,7 +550,7 @@ private function getHeaderPasteNewButton(ModelInterface $parentModel, Environmen $imageEvent = $dispatcher->dispatch( new GenerateHtmlEvent( 'new.svg', - $this->translate('pastenew.0', $parentDefinition->getName()) + $this->translateButtonLabel('pastenew', $parentDefinition->getName()) ), ContaoEvents::IMAGE_GET_HTML ); @@ -555,7 +558,7 @@ private function getHeaderPasteNewButton(ModelInterface $parentModel, Environmen return \sprintf( '%s', $urlEvent->getUrl(), - StringUtil::specialchars($this->translate('pastenew.0', $parentDefinition->getName())), + StringUtil::specialchars($this->translateButtonLabel('pastenew', $parentDefinition->getName())), $imageEvent->getHtml() ?? '' ); } @@ -593,7 +596,7 @@ private function getHeaderPasteTopButton(ModelInterface $parentModel, Environmen return null; } - if (!($allowPasteTop = ViewHelpers::getManualSortingProperty($environment))) { + if ($allowPasteTop = (bool) ViewHelpers::getManualSortingProperty($environment)) { $subFilter = new Filter(); $subFilter->andActionIsNotIn([ItemInterface::COPY, ItemInterface::DEEP_COPY]); $subFilter->andParentIsNot(ModelId::fromModel($parentModel)); @@ -634,7 +637,7 @@ private function getHeaderPasteTopButton(ModelInterface $parentModel, Environmen $imageEvent = $dispatcher->dispatch( new GenerateHtmlEvent( 'pasteafter.svg', - $this->translate('pasteafter.0', $definition->getName()), + $this->translateButtonLabel('pasteafter', $definition->getName()), 'class="blink"' ), ContaoEvents::IMAGE_GET_HTML @@ -643,7 +646,7 @@ private function getHeaderPasteTopButton(ModelInterface $parentModel, Environmen return \sprintf( '%s', $urlEvent->getUrl(), - StringUtil::specialchars($this->translate('pasteafter.0', $definition->getName())), + StringUtil::specialchars($this->translateButtonLabel('pasteafter', $definition->getName())), $imageEvent->getHtml() ?? '' ); } @@ -652,7 +655,7 @@ private function getHeaderPasteTopButton(ModelInterface $parentModel, Environmen $imageEvent = $dispatcher->dispatch( new GenerateHtmlEvent( 'pasteafter_.svg', - $this->translate('pasteafter.0', $definition->getName()), + $this->translateButtonLabel('pasteafter', $definition->getName()), 'class="blink"' ), ContaoEvents::IMAGE_GET_HTML diff --git a/src/Contao/View/Contao2BackendView/ActionHandler/SelectHandler.php b/src/Contao/View/Contao2BackendView/ActionHandler/SelectHandler.php index 8452c77f..1330216e 100644 --- a/src/Contao/View/Contao2BackendView/ActionHandler/SelectHandler.php +++ b/src/Contao/View/Contao2BackendView/ActionHandler/SelectHandler.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2023 Contao Community Alliance. + * (c) 2013-2024 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -16,7 +16,7 @@ * @author Stefan Heimes * @author Sven Baumann * @author Ingolf Steinhardt - * @copyright 2013-2023 Contao Community Alliance. + * @copyright 2013-2024 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -323,7 +323,7 @@ private function handleSelectPropertiesAllAction(EnvironmentInterface $environme * @param EnvironmentInterface $environment The environment. * @param Action $action The action. * - * @return null + * @return never * * @SuppressWarnings(PHPMD.UnusedPrivateMethod) */ @@ -344,8 +344,6 @@ private function handleDeleteAllAction(EnvironmentInterface $environment, Action } ViewHelpers::redirectHome($environment); - - return null; } /** @@ -354,7 +352,7 @@ private function handleDeleteAllAction(EnvironmentInterface $environment, Action * @param EnvironmentInterface $environment The environment. * @param Action $action The action. * - * @return null + * @return never * * @SuppressWarnings(PHPMD.UnusedPrivateMethod) */ @@ -372,8 +370,6 @@ private function handleCutAllAction(EnvironmentInterface $environment, Action $a } ViewHelpers::redirectHome($environment); - - return null; } /** @@ -382,7 +378,7 @@ private function handleCutAllAction(EnvironmentInterface $environment, Action $a * @param EnvironmentInterface $environment The environment. * @param Action $action The action. * - * @return null + * @return never * * @SuppressWarnings(PHPMD.UnusedPrivateMethod) */ @@ -400,8 +396,6 @@ private function handleCopyAllAction(EnvironmentInterface $environment, Action $ } ViewHelpers::redirectHome($environment); - - return null; } /** @@ -502,8 +496,8 @@ private function handleGlobalCommands(EnvironmentInterface $environment) $closeCommand ->setName('close_all_button') - ->setLabel('closeAll_label') - ->setDescription('closeAll_description') + ->setLabel('closeAll.label') + ->setDescription('closeAll.description') ->setParameters(new ArrayObject()) ->setExtra(new ArrayObject($closeExtra)) ->setDisabled(false); @@ -617,6 +611,9 @@ private function getSelectCollection(EnvironmentInterface $environment) foreach (($session['models'] ?? []) as $modelId) { $modelIds[] = ModelId::fromSerialized($modelId)->getId(); } + if ([] === $modelIds) { + return $dataProvider->getEmptyCollection(); + } $idProperty = method_exists($dataProvider, 'getIdProperty') ? $dataProvider->getIdProperty() : 'id'; $collection = $dataProvider->fetchAll( @@ -754,7 +751,7 @@ private function collectIntersectValues(CollectionInterface $collection, Environ $intersectValues = []; foreach ($values as $propertyName => $propertyValues) { - if (!($value = $this->getUniqueValueFromArray($propertyValues))) { + if (null === ($value = $this->getUniqueValueFromArray($propertyValues))) { continue; } @@ -863,6 +860,7 @@ private function handleSessionOverrideEditAll(array $collection, string $index, } // If the collection not in the session return the collection. + /** @psalm-suppress RiskyTruthyFalsyComparison */ if (empty($session[$index])) { return $collection; } diff --git a/src/Contao/View/Contao2BackendView/BaseView.php b/src/Contao/View/Contao2BackendView/BaseView.php index ca5ea06c..bf66d071 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-2023 Contao Community Alliance. + * (c) 2013-2024 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -19,7 +19,7 @@ * @author Martin Treml * @author Sven Baumann * @author Ingolf Steinhardt - * @copyright 2013-2023 Contao Community Alliance. + * @copyright 2013-2024 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -723,7 +723,7 @@ private function findOriginalPropertyByModelId(?string $propertyName): ?Property $originalPropertyName = \substr($propertyName, \strlen($propertyNamePrefix)); } - if (!$originalPropertyName) { + if (null === $originalPropertyName) { return null; } diff --git a/src/Contao/View/Contao2BackendView/ButtonRenderer.php b/src/Contao/View/Contao2BackendView/ButtonRenderer.php index 6fe8490d..00d5484f 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-2023 Contao Community Alliance. + * (c) 2013-2024 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-2023 Contao Community Alliance. + * @copyright 2013-2024 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -56,6 +56,20 @@ use ContaoCommunityAlliance\Translator\TranslatorInterface; use Symfony\Component\EventDispatcher\EventDispatcherInterface; +use function array_filter; +use function array_merge; +use function array_values; +use function implode; +use function in_array; +use function is_string; +use function ltrim; +use function sprintf; +use function str_replace; +use function strlen; +use function strrpos; +use function substr_replace; +use function trim; + /** * This class is a helper for rendering the operation buttons in the views. * @@ -147,10 +161,12 @@ public function __construct(EnvironmentInterface $environment) $this->circularModelIds = []; // We must only check for CUT operation here as pasting copy'ed parents is allowed. - $cutItems = \array_values(\array_filter( - $this->clipboardItems, - static fn (ItemInterface $item): bool => $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) { @@ -192,11 +208,11 @@ private function renderButtonsFor( ModelInterface $model, ModelInterface $previous = null, ModelInterface $next = null - ) { + ): void { $modelId = ModelId::fromModel($model)->getSerialized(); if ($this->clipboardItems) { - $isCircular = \in_array(ModelId::fromModel($model)->getSerialized(), $this->circularModelIds); + $isCircular = in_array(ModelId::fromModel($model)->getSerialized(), $this->circularModelIds); } else { $isCircular = false; } @@ -214,8 +230,8 @@ private function renderButtonsFor( // Add paste into/after icons. if ($this->hasPasteButtons()) { - $urlAfter = $this->addToUrl(\sprintf('act=paste&after=%s&', $modelId)); - $urlInto = $this->addToUrl(\sprintf('act=paste&into=%s&', $modelId)); + $urlAfter = $this->addToUrl(sprintf('act=paste&after=%s&', $modelId)); + $urlInto = $this->addToUrl(sprintf('act=paste&into=%s&', $modelId)); $buttonEvent = new GetPasteButtonEvent($this->environment); @@ -244,7 +260,7 @@ private function renderButtonsFor( $model->setMeta( $model::OPERATION_BUTTONS, - \implode(' ', $buttons) + implode(' ', $buttons) ); } @@ -253,7 +269,7 @@ private function renderButtonsFor( * * @return bool */ - private function isHierarchical() + private function isHierarchical(): bool { $dataDefinition = $this->environment->getDataDefinition(); assert($dataDefinition instanceof ContainerInterface); @@ -268,7 +284,7 @@ private function isHierarchical() * * @return bool */ - private function hasPasteButtons() + private function hasPasteButtons(): bool { return ((true === (bool) ViewHelpers::getManualSortingProperty($this->environment)) && false === empty($this->clipboardItems)); @@ -279,7 +295,7 @@ private function hasPasteButtons() * * @return bool */ - private function hasPasteNewButton() + private function hasPasteNewButton(): bool { $environment = $this->environment; $definition = $environment->getDataDefinition(); @@ -305,21 +321,31 @@ private function hasPasteNewButton() * @param string[] $childIds The ids of all child models. * * @return string + * + * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ - private function buildCommand($command, $model, $previous, $next, $isCircularReference, $childIds) - { + private function buildCommand( + CommandInterface $command, + ModelInterface $model, + ?ModelInterface $previous, + ?ModelInterface $next, + bool $isCircularReference, + array $childIds + ): string { $extra = (array) $command->getExtra(); - $attributes = ''; - - if (!empty($extra['attributes'])) { - $attributes .= \sprintf($extra['attributes'], $model->getID()); + if ('' !== ($attributes = $extra['attributes'] ?? '')) { + // BC compatibility with legacy strings containing 'Edit item %s' + if (false === str_contains($attributes, '%id%')) { + $attributes = sprintf($attributes, $model->getID()); + } + $attributes = strtr($attributes, ['%id%' => $model->getId()]); } $icon = $extra['icon']; if ($command instanceof ToggleCommandInterface) { $iconDisabled = ($extra['icon_disabled'] ?? 'invisible.svg'); - $attributes .= \sprintf( + $attributes .= sprintf( ' onclick="Backend.getScrollOffset(); return BackendGeneral.toggleVisibility(this, \'%s\', \'%s\');"', Controller::addStaticUrlTo(System::urlEncode($icon)), Controller::addStaticUrlTo(System::urlEncode($iconDisabled)) @@ -330,6 +356,9 @@ private function buildCommand($command, $model, $previous, $next, $isCircularRef } } + $definitionName = $this->environment->getDataDefinition()?->getName(); + assert(is_string($definitionName)); + $buttonEvent = new GetOperationButtonEvent($this->environment); $buttonEvent ->setKey($command->getName()) @@ -337,7 +366,13 @@ private function buildCommand($command, $model, $previous, $next, $isCircularRef ->setObjModel($model) ->setAttributes($attributes) ->setLabel($this->getCommandLabel($command)) - ->setTitle(\sprintf($this->translate($command->getDescription()), $model->getID())) + ->setTitle( + $this->translateButtonDescription( + str_replace(['.description', '.1'], '', $command->getDescription()), + $definitionName, + ['%id%' => $model->getId()] + ) + ) ->setHref($this->calculateHref($command, $model)) ->setChildRecordIds($childIds) ->setCircularReference($isCircularReference) @@ -348,7 +383,7 @@ private function buildCommand($command, $model, $previous, $next, $isCircularRef if (null !== ($html = $buttonEvent->getHtml())) { // If the event created a button, use it. - return \trim($html); + return trim($html); } if ($buttonEvent->isDisabled()) { @@ -359,13 +394,13 @@ private function buildCommand($command, $model, $previous, $next, $isCircularRef if ($icon !== Image::getPath($icon)) { $iconDisabledSuffix = '_'; } - $icon = \substr_replace($icon, $iconDisabledSuffix, \strrpos($icon, '.') ?: \strlen($icon), 0); + $icon = substr_replace($icon, $iconDisabledSuffix, ((int) strrpos($icon, '.')) ?: strlen($icon), 0); } return $this->renderImageAsHtml( $icon, $buttonEvent->getLabel(), - \sprintf( + sprintf( 'title="%s" class="%s"', StringUtil::specialchars($this->translator->translate( 'dc_general_disabled', @@ -377,12 +412,12 @@ private function buildCommand($command, $model, $previous, $next, $isCircularRef ); } - return \sprintf( + return sprintf( ' %s', $command->getName(), $buttonEvent->getHref() ?? '', StringUtil::specialchars($buttonEvent->getTitle()), - \ltrim($buttonEvent->getAttributes()), + ltrim($buttonEvent->getAttributes()), $this->renderImageAsHtml($icon, $buttonEvent->getLabel()) ); } @@ -394,7 +429,7 @@ private function buildCommand($command, $model, $previous, $next, $isCircularRef * * @return string[] */ - private function getChildIds(ModelInterface $model) + private function getChildIds(ModelInterface $model): array { if (null === ($childCollections = $model->getMeta($model::CHILD_COLLECTIONS))) { return []; @@ -409,7 +444,7 @@ private function getChildIds(ModelInterface $model) } } - return \array_merge($ids, ...$childIds); + return array_merge($ids, ...$childIds); } /** @@ -420,7 +455,7 @@ private function getChildIds(ModelInterface $model) * * @return string[] */ - private function calculateParameters(CommandInterface $command, $serializedModelId) + private function calculateParameters(CommandInterface $command, string $serializedModelId): array { $parameters = (array) $command->getParameters(); if ($command instanceof ToggleCommandInterface) { @@ -449,9 +484,10 @@ private function calculateParameters(CommandInterface $command, $serializedModel return $parameters; } + /** @var array{idparam?: string} $extra */ $extra = (array) $command->getExtra(); - $parameters[($extra['idparam'] ?? null) ?: 'id'] = $serializedModelId; + $parameters[($extra['idparam'] ?? '') ?: 'id'] = $serializedModelId; return $parameters; } @@ -463,10 +499,17 @@ private function calculateParameters(CommandInterface $command, $serializedModel * * @return string */ - private function renderPasteNewFor($modelId) + private function renderPasteNewFor(string $modelId): string { - $label = \sprintf($this->translate('pastenew.1'), ModelId::fromSerialized($modelId)->getId()); - return \sprintf( + $definitionName = $this->environment->getDataDefinition()?->getName(); + assert(is_string($definitionName)); + $label = $this->translateButtonDescription( + 'pastenew', + $definitionName, + ['%id%' => ModelId::fromSerialized($modelId)->getId()] + ); + + return sprintf( '%s', $this->addToUrl('act=create&after=' . $modelId), StringUtil::specialchars($label), @@ -481,13 +524,16 @@ private function renderPasteNewFor($modelId) * * @return string */ - private function renderPasteIntoButton(GetPasteButtonEvent $event) + private function renderPasteIntoButton(GetPasteButtonEvent $event): string { if (null !== ($value = $event->getHtmlPasteInto())) { return $value; } - $label = $this->translate('pasteinto.0'); + $definitionName = $event->getEnvironment()->getDataDefinition()?->getName(); + assert(is_string($definitionName)); + + $label = $this->translateButtonLabel('pasteinto', $definitionName); if ($event->isPasteIntoDisabled()) { return $this->renderImageAsHtml('pasteinto_.svg', $label, 'class="blink"'); } @@ -495,13 +541,9 @@ private function renderPasteIntoButton(GetPasteButtonEvent $event) $model = $event->getModel(); assert($model instanceof ModelInterface); - if ('pasteinto.1' !== ($opDesc = $this->translate('pasteinto.1'))) { - $title = \sprintf($opDesc, $model->getId()); - } else { - $title = \sprintf('%s id %s', $label, $model->getId()); - } + $title = $this->translateButtonDescription('pasteinto', $definitionName, ['%id%' => $model->getId()]); - return \sprintf( + return sprintf( ' %s', $event->getHrefInto() ?? '', StringUtil::specialchars($title), @@ -516,27 +558,24 @@ private function renderPasteIntoButton(GetPasteButtonEvent $event) * * @return string */ - private function renderPasteAfterButton(GetPasteButtonEvent $event) + private function renderPasteAfterButton(GetPasteButtonEvent $event): string { if (null !== ($value = $event->getHtmlPasteAfter())) { return $value; } - $label = $this->translate('pasteafter.0'); - if ($event->isPasteAfterDisabled()) { - return $this->renderImageAsHtml('pasteafter_.svg', $label, 'class="blink"'); - } - + $definitionName = $event->getEnvironment()->getDataDefinition()?->getName(); + assert(is_string($definitionName)); $model = $event->getModel(); assert($model instanceof ModelInterface); - if ('pasteafter.1' !== ($opDesc = $this->translate('pasteafter.1'))) { - $title = \sprintf($opDesc, $model->getId()); - } else { - $title = \sprintf('%s id %s', $label, $model->getId()); + $label = $this->translateButtonLabel('pasteafter', $definitionName, ['%id%' => $model->getId()]); + if ($event->isPasteAfterDisabled()) { + return $this->renderImageAsHtml('pasteafter_.svg', $label, 'class="blink"'); } + $title = $this->translateButtonDescription('pasteafter', $definitionName, ['%id%' => $model->getId()]); - return \sprintf( + return sprintf( ' %s', $event->getHrefAfter() ?? '', StringUtil::specialchars($title), @@ -549,7 +588,7 @@ private function renderPasteAfterButton(GetPasteButtonEvent $event) * * @return list */ - private function calculateClipboardItems() + private function calculateClipboardItems(): array { $dataDefinition = $this->environment->getDataDefinition(); assert($dataDefinition instanceof ContainerInterface); @@ -561,10 +600,10 @@ private function calculateClipboardItems() $filter = new Filter(); $dataProvider = $basicDefinition->getDataProvider(); - assert(\is_string($dataProvider)); + assert(is_string($dataProvider)); $filter->andModelIsFromProvider($dataProvider); - if ($parentProviderName = $basicDefinition->getParentDataProvider()) { + if (null !== ($parentProviderName = $basicDefinition->getParentDataProvider())) { $filter->andParentIsFromProvider($parentProviderName); } else { $filter->andHasNoParent(); @@ -593,6 +632,49 @@ protected function translate($path) return $this->translator->translate($path, 'dc-general'); } + protected function translateButtonLabel(string $buttonName, string $definitionName, array $parameter = []): string + { + // New way via symfony translator. + if ( + $buttonName . '.label' !== ($header = + $this->translator->translate($buttonName . '.label', $definitionName, $parameter)) + ) { + return $header; + } + + return $this->translator->translate($buttonName . '.0', $definitionName, $parameter); + } + + protected function translateButtonDescription( + string $buttonName, + string $definitionName, + array $parameter = [] + ): string { + // New way via symfony translator. + if ( + $buttonName . '.description' + !== ($header = $this->translator->translate($buttonName . '.description', $definitionName, $parameter)) + ) { + return $header; + } + + if ( + 1 !== preg_match('#%(?:[bcdeEfFgGhHosuxX]|\d*\$[bcdeEfFgGhHosuxX])#', $buttonName) + && $definitionName . '.' . $buttonName . '.1' + !== ( + $header = $this->translator->translate( + $definitionName . '.' . $buttonName . '.1', + 'contao_' . $definitionName, + $parameter + ) + ) + ) { + return $header; + } + + return vsprintf($buttonName, $parameter); + } + /** * Render an image as HTML string. * @@ -602,7 +684,7 @@ protected function translate($path) * * @return string */ - private function renderImageAsHtml($src, $alt, $attributes = '') + private function renderImageAsHtml(string $src, string $alt, string $attributes = ''): string { /** @var GenerateHtmlEvent $imageEvent */ $imageEvent = $this->eventDispatcher->dispatch( @@ -620,7 +702,7 @@ private function renderImageAsHtml($src, $alt, $attributes = '') * * @return string */ - private function addToUrl($parameters) + private function addToUrl(string $parameters): string { /** @var AddToUrlEvent $urlAfter */ $urlAfter = $this->eventDispatcher->dispatch(new AddToUrlEvent($parameters), ContaoEvents::BACKEND_ADD_TO_URL); @@ -636,7 +718,7 @@ private function addToUrl($parameters) * * @return bool */ - private function isTogglerInActiveState($command, $model) + private function isTogglerInActiveState(ToggleCommandInterface $command, ModelInterface $model): bool { $dataProvider = $this->environment->getDataProvider($model->getProviderName()); $propModel = $model; @@ -675,12 +757,12 @@ private function isTogglerInActiveState($command, $model) * * @return string */ - private function calculateHref(CommandInterface $command, $model) + private function calculateHref(CommandInterface $command, ModelInterface $model): string { $parameters = $this->calculateParameters($command, ModelId::fromModel($model)->getSerialized()); $href = ''; foreach ($parameters as $key => $value) { - $href .= \sprintf('&%s=%s', $key, $value); + $href .= sprintf('&%s=%s', $key, $value); } return $this->addToUrl($href); @@ -693,7 +775,7 @@ private function calculateHref(CommandInterface $command, $model) * * @return string */ - private function getCommandLabel(CommandInterface $command) + private function getCommandLabel(CommandInterface $command): string { if ('' === $label = $command->getLabel()) { $label = $command->getName(); diff --git a/src/Contao/View/Contao2BackendView/Controller/ClipboardController.php b/src/Contao/View/Contao2BackendView/Controller/ClipboardController.php index b393ee96..c2f21364 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-2023 Contao Community Alliance. + * (c) 2013-2024 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 Stefan Heimes * @author Sven Baumann * @author Ingolf Steinhardt - * @copyright 2013-2023 Contao Community Alliance. + * @copyright 2013-2024 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -46,9 +46,16 @@ use ContaoCommunityAlliance\DcGeneral\Event\ViewEvent; use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; use ContaoCommunityAlliance\Translator\TranslatorInterface; +use ContaoCommunityAlliance\UrlBuilder\UrlBuilder; use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\EventDispatcher\EventSubscriberInterface; +use function array_shift; +use function in_array; +use function is_string; +use function parse_str; +use function sprintf; + /** * Class ClipboardController. * @@ -61,7 +68,7 @@ class ClipboardController implements EventSubscriberInterface * * @var RequestScopeDeterminator */ - private $scopeDeterminator; + private RequestScopeDeterminator $scopeDeterminator; /** * ClipboardController constructor. @@ -143,7 +150,7 @@ private function checkPermission(ActionEvent $event) if ( (('create' === $actionName) && (true === $basicDefinition->isCreatable())) || (('cut' === $actionName) && (true === $basicDefinition->isEditable())) - || (false === \in_array($actionName, ['create', 'cut'])) + || (false === in_array($actionName, ['create', 'cut'])) ) { return true; } @@ -162,7 +169,7 @@ private function checkPermission(ActionEvent $event) } $event->setResponse( - \sprintf( + sprintf( '
%s.
', $permissionMessage ) @@ -206,7 +213,15 @@ private function clearClipboard(ActionEvent $event, $redirect = true) $addToUrlEvent = new AddToUrlEvent('clipboard-item=&original-act=&act=' . $input->getParameter('original-act')); $eventDispatcher->dispatch($addToUrlEvent, ContaoEvents::BACKEND_ADD_TO_URL); - $redirectEvent = new RedirectEvent($addToUrlEvent->getUrl()); + $url = new UrlBuilder($addToUrlEvent->getUrl()); + parse_str($url->getQueryString() ?? '', $parameters); + foreach ($parameters as $name => $value) { + if ('' === $value) { + $url->unsetQueryParameter($name); + } + } + + $redirectEvent = new RedirectEvent($url->getUrl()); $eventDispatcher->dispatch($redirectEvent, ContaoEvents::CONTROLLER_REDIRECT); } @@ -259,7 +274,7 @@ private function addToClipboard(ActionEvent $event) $parentId = null; } - if (!($clipboardActionName = $this->translateActionName($actionName))) { + if (null === ($clipboardActionName = $this->translateActionName($actionName))) { return; } @@ -272,11 +287,11 @@ private function addToClipboard(ActionEvent $event) assert($definition instanceof ContainerInterface); $providerName = $definition->getBasicDefinition()->getDataProvider(); - assert(\is_string($providerName)); + assert(is_string($providerName)); - $item = new UnsavedItem($clipboardActionName, $parentId, $providerName); + $item = new UnsavedItem($clipboardActionName, $parentId, $providerName); - // Remove other create items, there can only be one create item in the clipboard or many others + // Remove other create items, there can only be one create item in the clipboard or many others. $clipboard->clear(); } else { $modelIdRaw = $input->getParameter('source'); @@ -286,7 +301,7 @@ private function addToClipboard(ActionEvent $event) $this->removeItemsFromClipboard($event); // Only push item to clipboard if manual sorting is used. - if (Item::COPY === $clipboardActionName && !ViewHelpers::getManualSortingProperty($environment)) { + if (Item::COPY === $clipboardActionName && null === ViewHelpers::getManualSortingProperty($environment)) { return; } @@ -321,7 +336,7 @@ protected function isAddingAllowed(EnvironmentInterface $environment) // 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. - return (!ViewHelpers::getManualSortingProperty($environment) + return (null === ViewHelpers::getManualSortingProperty($environment) || $inputProvider->hasParameter('after') || $inputProvider->hasParameter('into') ); @@ -358,11 +373,11 @@ public function handleView(ViewEvent $event) $basicDefinition = $definition->getBasicDefinition(); $dataProvider = $basicDefinition->getDataProvider(); - assert(\is_string($dataProvider)); + assert(is_string($dataProvider)); $filter = new Filter(); $filter->andModelIsFromProvider($dataProvider); - if ($parentProviderName = $basicDefinition->getParentDataProvider()) { + if (null !== ($parentProviderName = $basicDefinition->getParentDataProvider())) { $filter->andParentIsFromProvider($parentProviderName); } else { $filter->andHasNoParent(); @@ -390,7 +405,7 @@ public function handleView(ViewEvent $event) $formatModelLabel = new FormatModelLabelEvent($environment, $model); $eventDispatcher->dispatch($formatModelLabel, DcGeneralEvents::FORMAT_MODEL_LABEL); $label = $formatModelLabel->getLabel(); - $label = \array_shift($label); + $label = array_shift($label); $label = $label['content']; } else { $model = $dataProvider->getEmptyModel(); @@ -398,7 +413,7 @@ public function handleView(ViewEvent $event) $translator = $environment->getTranslator(); assert($translator instanceof TranslatorInterface); - $label = $translator->translate('new.0', $item->getDataProviderName()); + $label = $translator->translate('new.label', $item->getDataProviderName()); } $options[$item->getClipboardId()] = ['item' => $item, 'model' => $model, 'label' => $label]; diff --git a/src/Contao/View/Contao2BackendView/EditMask.php b/src/Contao/View/Contao2BackendView/EditMask.php index 5e2b3287..da3ae1b5 100644 --- a/src/Contao/View/Contao2BackendView/EditMask.php +++ b/src/Contao/View/Contao2BackendView/EditMask.php @@ -787,7 +787,7 @@ protected function doPersist() return false; } - if ((null === $this->model->getId()) && $this->getManualSortingProperty()) { + if ((null === $this->model->getId()) && null !== $this->getManualSortingProperty()) { $models = $dataProvider->getEmptyCollection(); $models->push($this->model); diff --git a/src/Contao/View/Contao2BackendView/EventListener/ColorPickerWizardListener.php b/src/Contao/View/Contao2BackendView/EventListener/ColorPickerWizardListener.php index b67b598d..0d7d6832 100644 --- a/src/Contao/View/Contao2BackendView/EventListener/ColorPickerWizardListener.php +++ b/src/Contao/View/Contao2BackendView/EventListener/ColorPickerWizardListener.php @@ -95,7 +95,7 @@ public static function getWizard($propInfo, EnvironmentInterface $environment) assert($translator instanceof TranslatorInterface); if (\array_key_exists('colorpicker', $propExtra) && $propExtra['colorpicker']) { - $pickerText = $translator->translate('colorpicker', 'dc-general'); + $pickerText = $translator->translate('colorPicker', 'dc-general'); $event = new GenerateHtmlEvent( 'pickcolor.svg', $pickerText, diff --git a/src/Contao/View/Contao2BackendView/EventListener/CreateModelButtonListener.php b/src/Contao/View/Contao2BackendView/EventListener/CreateModelButtonListener.php index fd5d40e0..c314af49 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-2023 Contao Community Alliance. + * (c) 2013-2024 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-2023 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2024 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -80,7 +81,7 @@ public function handle(GetGlobalButtonEvent $event) assert(\is_string($provider)); $filter->andModelIsFromProvider($provider); - if ($parentProviderName = $basicDefinition->getParentDataProvider()) { + if (null !== ($parentProviderName = $basicDefinition->getParentDataProvider())) { $filter->andParentIsFromProvider($parentProviderName); } else { $filter->andHasNoParent(); diff --git a/src/Contao/View/Contao2BackendView/EventListener/SelectModeButtonsListener.php b/src/Contao/View/Contao2BackendView/EventListener/SelectModeButtonsListener.php index 838a5cbe..7ebbdc77 100644 --- a/src/Contao/View/Contao2BackendView/EventListener/SelectModeButtonsListener.php +++ b/src/Contao/View/Contao2BackendView/EventListener/SelectModeButtonsListener.php @@ -90,7 +90,7 @@ public function handleEvent(GetSelectModeButtonsEvent $event) } $sortingProperty = ViewHelpers::getManualSortingProperty($event->getEnvironment()); - if ($sortingProperty && $basicDefinition->isEditable()) { + if (null !== $sortingProperty && $basicDefinition->isEditable()) { $buttons['cut'] = \sprintf( $input, 'cut', diff --git a/src/Contao/View/Contao2BackendView/FileSelect.php b/src/Contao/View/Contao2BackendView/FileSelect.php index b3bd27c9..76b2a928 100644 --- a/src/Contao/View/Contao2BackendView/FileSelect.php +++ b/src/Contao/View/Contao2BackendView/FileSelect.php @@ -362,7 +362,7 @@ private function setupItemContainer(ModelIdInterface $modelId) } /** - * Run the ajax request if is determine for run. + * Run the ajax request if it is determining for run. * * @SuppressWarnings(PHPMD.Superglobals) * diff --git a/src/Contao/View/Contao2BackendView/GlobalButtonRenderer.php b/src/Contao/View/Contao2BackendView/GlobalButtonRenderer.php index ce2bb4a6..c8d4efac 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-2023 Contao Community Alliance. + * (c) 2013-2024 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-2023 Contao Community Alliance. + * @copyright 2013-2024 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -44,21 +44,21 @@ class GlobalButtonRenderer * * @var EnvironmentInterface */ - private $environment; + private EnvironmentInterface $environment; /** * The dispatcher. * * @var EventDispatcherInterface */ - private $dispatcher; + private EventDispatcherInterface $dispatcher; /** * The translator. * * @var TranslatorInterface */ - private $translator; + private TranslatorInterface $translator; /** * Create a new instance. @@ -115,11 +115,26 @@ public function render() * @param CommandInterface $command The command definition. * * @return string + * + * @SuppressWarnings(PHPMD.CyclomaticComplexity) + * @SuppressWarnings(PHPMD.NPathComplexity) */ private function renderButton(CommandInterface $command) { $extra = $command->getExtra(); $label = $this->translate($command->getLabel()); + // Translation fallback to old Contao translations. + // @deprecated Remove in 3.0 + if (str_ends_with($label, '.label') && $label === $command->getLabel()) { + $label = $this->translate(substr($command->getLabel(), 0, -6) . '.0'); + } + + $description = $this->translate($command->getDescription()); + // Translation fallback to old Contao translations. + // @deprecated Remove in 3.0 + if (str_ends_with($description, '.description') && $description === $command->getDescription()) { + $description = $this->translate(substr($command->getDescription(), 0, -12) . '.1'); + } if (isset($extra['href'])) { $href = $extra['href']; @@ -146,11 +161,11 @@ private function renderButton(CommandInterface $command) $buttonEvent ->setAccessKey(isset($extra['accesskey']) ? \trim($extra['accesskey']) : '') ->setAttributes(' ' . \ltrim($extra['attributes'] ?? '')) - ->setClass($extra['class']) + ->setClass($extra['class'] ?? '') ->setKey($command->getName()) ->setHref($href) ->setLabel($label) - ->setTitle($this->translate($command->getDescription())); + ->setTitle($description); $this->dispatcher->dispatch($buttonEvent, GetGlobalButtonEvent::NAME); // Allow to override the button entirely - if someone sets empty string, we keep it. @@ -181,8 +196,16 @@ private function translate(string $path): string $definition = $this->environment->getDataDefinition(); assert($definition instanceof ContainerInterface); - $value = $this->translator->translate($path, $definition->getName()); - if ($path !== $value) { + $domain = $definition->getName(); + if ($path !== ($value = $this->translator->translate($path, $domain))) { + return $value; + } + + // Fallback translate for non symfony domain. + if ( + $domain . '.' . $path !== ($value = + $this->translator->translate($domain . '.' . $path, 'contao_' . $domain)) + ) { return $value; } diff --git a/src/Contao/View/Contao2BackendView/Subscriber/CheckPermission.php b/src/Contao/View/Contao2BackendView/Subscriber/CheckPermission.php index 89011350..cbf9ed79 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-2023 Contao Community Alliance. + * (c) 2013-2024 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,14 @@ * @author Sven Baumann * @author Christian Schiffler * @author Ingolf Steinhardt - * @copyright 2013-2023 Contao Community Alliance. + * @copyright 2013-2024 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ namespace ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Subscriber; +use Contao\CoreBundle\Security\ContaoCorePermissions; use ContaoCommunityAlliance\DcGeneral\Contao\DataDefinition\Definition\Contao2BackendViewDefinitionInterface; use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminator; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\CommandCollectionInterface; @@ -30,6 +31,7 @@ use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\PropertyInterface; use ContaoCommunityAlliance\DcGeneral\Factory\Event\BuildDataDefinitionEvent; use Symfony\Component\EventDispatcher\EventSubscriberInterface; +use Symfony\Component\Security\Core\Security; /** * The check permission subscriber. @@ -41,15 +43,18 @@ class CheckPermission implements EventSubscriberInterface * * @var RequestScopeDeterminator */ - private $scopeDeterminator; + private RequestScopeDeterminator $scopeDeterminator; /** * ClipboardController constructor. * - * @param RequestScopeDeterminator $scopeDeterminator + * @param RequestScopeDeterminator $scopeDeterminator The request mode determinator. + * @param Security $security The security. */ - public function __construct(RequestScopeDeterminator $scopeDeterminator) - { + public function __construct( + RequestScopeDeterminator $scopeDeterminator, + private Security $security + ) { $this->scopeDeterminator = $scopeDeterminator; } @@ -84,6 +89,7 @@ public function checkPermissionForProperties(BuildDataDefinitionEvent $event) $container = $event->getContainer(); $properties = $container->getPropertiesDefinition(); $palettesDefinition = $container->getPalettesDefinition(); + $definitionName = $container->getName(); foreach ($palettesDefinition->getPalettes() as $palette) { foreach ($palette->getProperties() as $property) { @@ -100,10 +106,21 @@ public function checkPermissionForProperties(BuildDataDefinitionEvent $event) // @codingStandardsIgnoreEnd continue; } + $excluded = $properties->getProperty($name)->isExcluded(); + // Include all excluded fields which are allowed for the current user. + if ( + $excluded + && $this->security->isGranted( + ContaoCorePermissions::USER_CAN_EDIT_FIELD_OF_TABLE, + $definitionName . '::' . $name + ) + ) { + $excluded = false; + } $this ->getVisibilityConditionChain($property) - ->addCondition(new BooleanCondition(!$properties->getProperty($name)->isExcluded())); + ->addCondition(new BooleanCondition(!$excluded)); } } } @@ -198,7 +215,7 @@ public function checkPermissionIsCreatable(BuildDataDefinitionEvent $event) * * @return PropertyConditionChain */ - private function getVisibilityConditionChain($property) + private function getVisibilityConditionChain(PropertyInterface $property): PropertyConditionChain { if ( ($chain = $property->getVisibleCondition()) @@ -222,7 +239,7 @@ private function getVisibilityConditionChain($property) * * @return void */ - private function disableCommandByActionName(CommandCollectionInterface $commands, $actionName) + private function disableCommandByActionName(CommandCollectionInterface $commands, string $actionName): void { foreach ($commands->getCommands() as $command) { $parameters = $command->getParameters()->getArrayCopy(); @@ -252,7 +269,7 @@ private function disableCommandByActionName(CommandCollectionInterface $commands * * @return void */ - private function disableToggleCommand(CommandCollectionInterface $commands) + private function disableToggleCommand(CommandCollectionInterface $commands): void { foreach ($commands->getCommands() as $command) { if (!($command instanceof ToggleCommandInterface)) { diff --git a/src/Contao/View/Contao2BackendView/Subscriber/GetGroupHeaderSubscriber.php b/src/Contao/View/Contao2BackendView/Subscriber/GetGroupHeaderSubscriber.php index 10c5d207..97f7863b 100644 --- a/src/Contao/View/Contao2BackendView/Subscriber/GetGroupHeaderSubscriber.php +++ b/src/Contao/View/Contao2BackendView/Subscriber/GetGroupHeaderSubscriber.php @@ -81,7 +81,7 @@ public function __construct(EventDispatcherInterface $dispatcher, TranslatorInte * * @return void */ - public function handle(GetGroupHeaderEvent $event) + public function handle(GetGroupHeaderEvent $event): void { if ((null !== $event->getValue()) || !$this->getScopeDeterminator()->currentScopeIsBackend()) { return; @@ -130,7 +130,7 @@ protected function formatGroupHeader( PropertyInterface $property, $groupingMode, $groupingLength - ) { + ): ?string { $evaluation = $property->getExtra(); if (isset($evaluation['multiple']) && !$evaluation['multiple'] && ('checkbox' === $property->getWidgetType())) { @@ -156,7 +156,7 @@ protected function formatGroupHeader( $remoteNew = $remoteNew[0]; } - if (empty($remoteNew)) { + if ('' === (string) $remoteNew) { $remoteNew = '-'; } @@ -170,7 +170,7 @@ protected function formatGroupHeader( * * @return string */ - private function formatCheckboxOptionLabel($value) + private function formatCheckboxOptionLabel(string $value): string { return \ucfirst($this->translator->translate(('' !== $value) ? 'yes' : 'no', 'dc-general')); } @@ -192,7 +192,7 @@ private function formatByGroupingMode( EnvironmentInterface $environment, PropertyInterface $property, ModelInterface $model - ) { + ): ?string { switch ($groupingMode) { case GroupAndSortingInformationInterface::GROUP_CHAR: return $this->formatByCharGrouping( @@ -225,7 +225,7 @@ private function formatByGroupingMode( * * @return string */ - private function formatByCharGrouping($value, $groupingLength) + private function formatByCharGrouping($value, $groupingLength): string { if ('' === $value) { return '-'; @@ -304,7 +304,7 @@ private function formatByMonthGrouping(int $value): ?string * * @return string */ - private function formatByYearGrouping($value) + private function formatByYearGrouping(int $value): string { $value = $this->getTimestamp($value); diff --git a/src/Contao/View/Contao2BackendView/Subscriber/MultipleHandlerSubscriber.php b/src/Contao/View/Contao2BackendView/Subscriber/MultipleHandlerSubscriber.php index 87beaade..abe3a795 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-2023 Contao Community Alliance. + * (c) 2013-2024 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-2023 Contao Community Alliance. + * @copyright 2013-2024 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0 * @filesource */ @@ -184,7 +184,8 @@ public function handleOriginalOptions(GetOptionsEvent $event) $model = $event->getModel(); if ( - !($propertyName = $this->getOriginalPropertyName($event->getPropertyName(), ModelId::fromModel($model))) + null === ($propertyName = + $this->getOriginalPropertyName($event->getPropertyName(), ModelId::fromModel($model))) || !$model->getProperty($propertyName) ) { return; diff --git a/src/Contao/View/Contao2BackendView/Subscriber/WidgetBuilder.php b/src/Contao/View/Contao2BackendView/Subscriber/WidgetBuilder.php index 068fa3c6..a3303bee 100644 --- a/src/Contao/View/Contao2BackendView/Subscriber/WidgetBuilder.php +++ b/src/Contao/View/Contao2BackendView/Subscriber/WidgetBuilder.php @@ -56,6 +56,7 @@ * Widget Builder build Contao backend widgets. * * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) + * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.CouplingBetweenObjects) * * @final @@ -546,18 +547,33 @@ private function prepareWidgetAttributes(ModelInterface $model, PropertyInterfac $propExtra = $this->setPropExtraDisabled($property, $propExtra); + // If no description present, pass as string instead of array. + $label = $this->translator->trans($property->getLabel(), [], $defName); + if ('' !== $description = $property->getDescription()) { + $label = [ + $label, + $this->translator->trans($description, [], $defName), + ]; + } + $widgetConfig = [ 'inputType' => $property->getWidgetType(), - 'label' => [ - $this->translator->trans($property->getLabel(), [], $defName), - $this->translator->trans($property->getDescription(), [], $defName), - ], + 'label' => $label, 'options' => $this->getOptionsForWidget($property, $model), 'eval' => $propExtra, ]; if (isset($propExtra['reference'])) { - $widgetConfig['reference'] = $propExtra['reference']; + $references = []; + foreach ($propExtra['reference'] as $refName => $refLabelKey) { + if (!is_string($refLabelKey)) { + $references[$refName] = $refName; + continue; + } + $references[$refName] = $this->translator->trans($refLabelKey, [], $defName); + } + $widgetConfig['reference'] = $references; + unset($references, $refName, $refLabelKey); } $event = new GetAttributesFromDcaEvent( diff --git a/src/Contao/View/Contao2BackendView/TreePicker.php b/src/Contao/View/Contao2BackendView/TreePicker.php index 04609c1e..f278346d 100644 --- a/src/Contao/View/Contao2BackendView/TreePicker.php +++ b/src/Contao/View/Contao2BackendView/TreePicker.php @@ -423,7 +423,7 @@ public function __set($key, $value) */ private function convertValue($value) { - if (empty($value)) { + if (null === $value || '' === $value) { return null; } @@ -1208,7 +1208,7 @@ private function prepareFilterForRootCondition() $baseFilter = $baseConfig->getFilter(); $filter = $rootCondition->getFilterArray(); - if ($baseFilter) { + if (null !== $baseFilter) { $filter = \array_merge($baseFilter, $filter); } @@ -1705,7 +1705,7 @@ private function handleInputNameForEditAll() $originalPropertyName = \substr($this->strName, \strlen($propertyNamePrefix)); } - if (!$originalPropertyName) { + if (null === $originalPropertyName) { return; } diff --git a/src/Contao/View/Contao2BackendView/TreeView.php b/src/Contao/View/Contao2BackendView/TreeView.php index 6a839cb0..b7c3e513 100644 --- a/src/Contao/View/Contao2BackendView/TreeView.php +++ b/src/Contao/View/Contao2BackendView/TreeView.php @@ -36,7 +36,6 @@ use ContaoCommunityAlliance\DcGeneral\Clipboard\Filter; use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminator; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\GetPasteRootButtonEvent; -use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\ModelToLabelEvent; use ContaoCommunityAlliance\DcGeneral\Controller\ControllerInterface; use ContaoCommunityAlliance\DcGeneral\Controller\ModelCollector; use ContaoCommunityAlliance\DcGeneral\Controller\TreeCollector; @@ -270,7 +269,7 @@ public function loadCollection($rootId = null, $level = 0, $providerName = null) $inputProvider = $environment->getInputProvider(); assert($inputProvider instanceof InputProviderInterface); - $collection = $rootId + $collection = null !== $rootId ? $collector->getTreeCollectionRecursive($rootId, $level, $realProvider) : $collector->getChildrenOf( $realProvider, @@ -278,7 +277,7 @@ public function loadCollection($rootId = null, $level = 0, $providerName = null) $inputProvider->hasParameter('pid') ? $this->loadParentModel() : null ); - if ($rootId) { + if (null !== $rootId) { $treeData = $dataDriver->getEmptyCollection(); $model = $collection->get(0); assert($model instanceof ModelInterface); @@ -495,10 +494,9 @@ public static function renderPasteRootButton(GetPasteRootButtonEvent $event) $definition = $environment->getDataDefinition(); assert($definition instanceof ContainerInterface); - $label = $translator->translate( - 'pasteinto.0', - $definition->getName() - ); + if ('pasteinto.label' === ($label = $translator->translate('pasteinto.label', $definition->getName()))) { + $label = $translator->translate('pasteinto.0', $definition->getName()); + } $dispatcher = $environment->getEventDispatcher(); assert($dispatcher instanceof EventDispatcherInterface); @@ -568,10 +566,10 @@ protected function viewTree($collection) } // Label + Icon. - if (null === $listing->getRootLabel()) { + if (null === ($label = $listing->getRootLabel())) { $labelText = 'DC General Tree BackendView Ultimate'; } else { - $labelText = $listing->getRootLabel(); + $labelText = $this->translate($label, $definition->getName()); } if (null === $listing->getRootIcon()) { @@ -586,7 +584,7 @@ protected function viewTree($collection) assert(\is_string($dataProvider)); $filter->andModelIsFromProvider($dataProvider); - if ($parentProviderName = $basicDefinition->getParentDataProvider()) { + if (null !== ($parentProviderName = $basicDefinition->getParentDataProvider())) { $filter->andParentIsFromProvider($parentProviderName); } else { $filter->andHasNoParent(); diff --git a/src/Contao/View/Contao2BackendView/ViewHelpers.php b/src/Contao/View/Contao2BackendView/ViewHelpers.php index ac64737e..a33f08ef 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-2023 Contao Community Alliance. + * (c) 2013-2024 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -17,13 +17,14 @@ * @author Stefan Heimes * @author Sven Baumann * @author Ingolf Steinhardt - * @copyright 2013-2023 Contao Community Alliance. + * @copyright 2013-2024 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ namespace ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView; +use Contao\System; use ContaoCommunityAlliance\Contao\Bindings\ContaoEvents; use ContaoCommunityAlliance\Contao\Bindings\Events\Controller\RedirectEvent; use ContaoCommunityAlliance\DcGeneral\Contao\DataDefinition\Definition\Contao2BackendViewDefinitionInterface; @@ -40,7 +41,14 @@ use ContaoCommunityAlliance\DcGeneral\Panel\PanelInterface; use ContaoCommunityAlliance\DcGeneral\Panel\SortElementInterface; use ContaoCommunityAlliance\DcGeneral\View\Event\RenderReadablePropertyValueEvent; +use LogicException; use Symfony\Component\EventDispatcher\EventDispatcherInterface; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\RequestStack; +use Symfony\Component\Routing\Generator\UrlGeneratorInterface; + +use function sprintf; +use function strtoupper; /** * Helper class that provides static methods used in views. @@ -157,7 +165,7 @@ public static function initializeSorting($panel, $dataConfig, $listingConfig) $newSorting = []; foreach ($listingConfig->getGroupAndSortingDefinition()->getDefault() as $information) { /** @var GroupAndSortingInformationInterface $information */ - $newSorting[$information->getProperty()] = \strtoupper($information->getSortingMode()); + $newSorting[$information->getProperty()] = strtoupper($information->getSortingMode()); } $dataConfig->setSorting($newSorting); } @@ -240,44 +248,77 @@ public static function getReadableFieldValue( * * @param EnvironmentInterface $environment The environment. * - * @return void + * @return never */ - public static function redirectHome(EnvironmentInterface $environment) + public static function redirectHome(EnvironmentInterface $environment): never { $input = $environment->getInputProvider(); assert($input instanceof InputProviderInterface); + $request = self::getRequest(); + $routeName = $request->attributes->get('_route'); + if ($routeName !== 'contao.backend') { + self::determineNewStyleRedirect($routeName, $request, $environment); + } + if ($input->hasParameter('table')) { if ($input->hasParameter('pid')) { $event = new RedirectEvent( - \sprintf( + sprintf( 'contao?do=%s&table=%s&pid=%s', $input->getParameter('do'), $input->getParameter('table'), $input->getParameter('pid') ) ); - } else { - $event = new RedirectEvent( - \sprintf( - 'contao?do=%s&table=%s', - $input->getParameter('do'), - $input->getParameter('table') - ) - ); + self::dispatchRedirect($environment, $event); } - } else { $event = new RedirectEvent( - \sprintf( - 'contao?do=%s', - $input->getParameter('do') + sprintf( + 'contao?do=%s&table=%s', + $input->getParameter('do'), + $input->getParameter('table') ) ); + self::dispatchRedirect($environment, $event); } + $event = new RedirectEvent(sprintf('contao?do=%s', $input->getParameter('do'))); + + self::dispatchRedirect($environment, $event); + } + + private static function determineNewStyleRedirect( + string $routeName, + Request $request, + EnvironmentInterface $environment + ): never { + $routeGenerator = System::getContainer()->get('router'); + assert($routeGenerator instanceof UrlGeneratorInterface); + $parameters = $request->query->all(); + unset($parameters['act']); + $routeBase = $routeGenerator->generate($routeName, $parameters); + + self::dispatchRedirect($environment, new RedirectEvent($routeBase)); + } + + private static function getRequest(): Request + { + $requestStack = System::getContainer()->get('request_stack'); + assert($requestStack instanceof RequestStack); + $request = $requestStack->getCurrentRequest(); + assert($request instanceof Request); + + return $request; + } + + public static function dispatchRedirect(EnvironmentInterface $environment, RedirectEvent $event): never + { $dispatcher = $environment->getEventDispatcher(); assert($dispatcher instanceof EventDispatcherInterface); $dispatcher->dispatch($event, ContaoEvents::CONTROLLER_REDIRECT); + + throw new LogicException('Redirect did not happen.'); } } diff --git a/src/Contao/View/Contao2BackendView/Widget/PageTree.php b/src/Contao/View/Contao2BackendView/Widget/PageTree.php index fd791252..335bd3f4 100644 --- a/src/Contao/View/Contao2BackendView/Widget/PageTree.php +++ b/src/Contao/View/Contao2BackendView/Widget/PageTree.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2023 Contao Community Alliance. + * (c) 2013-2024 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,7 @@ * @author Sven Baumann * @author Christian Schiffler * @author Ingolf Steinhardt - * @copyright 2013-2023 Contao Community Alliance. + * @copyright 2013-2024 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -83,7 +83,7 @@ public function loadCollection($rootId = null, $level = 0, $providerName = null) assert($dataProvider instanceof DataProviderInterface); $treeData = $dataProvider->getEmptyCollection(); - if ($rootId) { + if (null !== $rootId) { $objModel = $collection->get(0); assert($objModel instanceof ModelInterface); diff --git a/src/Controller/Ajax.php b/src/Controller/Ajax.php index d11c442d..544c4c52 100644 --- a/src/Controller/Ajax.php +++ b/src/Controller/Ajax.php @@ -234,6 +234,9 @@ public function executePostActions(DataContainerInterface $container) assert($inputProvider instanceof InputProviderInterface); $action = $inputProvider->getValue('action'); + if (null === $action) { + return; + } if ( \in_array( diff --git a/src/Controller/Ajax3X.php b/src/Controller/Ajax3X.php index 5ddf5824..1925f423 100644 --- a/src/Controller/Ajax3X.php +++ b/src/Controller/Ajax3X.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2023 Contao Community Alliance. + * (c) 2013-2024 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,7 @@ * @author David Molineus * @author Sven Baumann * @author Ingolf Steinhardt - * @copyright 2013-2023 Contao Community Alliance. + * @copyright 2013-2024 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -44,6 +44,16 @@ use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\HttpFoundation\Response; +use function array_merge; +use function implode; +use function is_string; +use function preg_replace; +use function str_replace; +use function str_starts_with; +use function strlen; +use function substr; +use function urldecode; + /** * Class GeneralAjax - General purpose Ajax handler for "executePostActions" in Contao 3.X as we can not use the default * Contao handling. @@ -83,14 +93,14 @@ protected function getWidget($fieldName, $serializedId, $propertyValue) $widgetManager = new ContaoWidgetManager($environment, $model); // Process input and update changed properties. - $treeType = \substr($property->getWidgetType(), 0, 4); + $treeType = substr($property->getWidgetType(), 0, 4); $propertyValue = $this->getTreeValue($treeType, $propertyValue); if (('file' === $treeType) || ('page' === $treeType)) { $extra = $property->getExtra(); if (!isset($extra['multiple'])) { $propertyValue = $propertyValue[0]; } else { - $propertyValue = \implode(',', $propertyValue); + $propertyValue = implode(',', $propertyValue); } } @@ -123,16 +133,16 @@ protected function loadPagetree() assert($definition instanceof ContainerInterface); $field = $input->getValue('field'); - $name = $input->getValue('name'); + $name = (string) $input->getValue('name'); $level = (int) $input->getValue('level'); $rootId = (string) $input->getValue('id'); - $ajaxId = \preg_replace('/.*_([0-9a-zA-Z]+)$/', '$1', $rootId); - $ajaxKey = \str_replace('_' . $ajaxId, '', $rootId); - $ajaxName = null; + $ajaxId = preg_replace('/.*_([0-9a-zA-Z]+)$/', '$1', $rootId); + $ajaxKey = str_replace('_' . $ajaxId, '', $rootId); + $ajaxName = ''; if ('editAll' === $input->getValue('act')) { - $ajaxKey = \preg_replace('/(.*)_[0-9a-zA-Z]+$/', '$1', $ajaxKey); - $ajaxName = \preg_replace('/.*_([0-9a-zA-Z]+)$/', '$1', $name); + $ajaxKey = preg_replace('/(.*)_[0-9a-zA-Z]+$/', '$1', $ajaxKey); + $ajaxName = (string) preg_replace('/.*_([0-9a-zA-Z]+)$/', '$1', $name); } $nodes = $session->get($ajaxKey); @@ -184,7 +194,7 @@ protected function loadFiletree() $arrData['strTable'] = $input->getParameter('table'); $arrData['id'] = $field; $arrData['name'] = $field; - $arrData = \array_merge( + $arrData = array_merge( $definition->getPropertiesDefinition()->getProperty($field)->getExtra(), $arrData ); @@ -227,8 +237,8 @@ protected function getTreeValue($type, $value) // Automatically add resources to the DBAFS. if ('file' === $type) { foreach ($value as $k => $v) { - $uuid = Dbafs::addResource(\urldecode($v))->uuid; - assert(\is_string($uuid)); + $uuid = Dbafs::addResource(urldecode($v))->uuid; + assert(is_string($uuid)); $value[$k] = StringUtil::binToUuid($uuid); } } @@ -293,7 +303,7 @@ protected function reloadTree() $value = $input->hasValue('value') ? $input->getValue('value', true) : ''; $fieldName = $this->getFieldName(); - assert(\is_string($fieldName)); + assert(is_string($fieldName)); $widget = $this->getWidget($fieldName, $serializedId, $value); assert($widget instanceof Widget); @@ -383,15 +393,15 @@ private function getFieldName() break; } - $propertyNamePrefix = \str_replace('::', '____', $modelId) . '_'; - if (0 !== strpos($fieldName, $propertyNamePrefix)) { + $propertyNamePrefix = str_replace('::', '____', $modelId) . '_'; + if (!str_starts_with($fieldName, $propertyNamePrefix)) { continue; } - $originalPropertyName = \substr($fieldName, \strlen($propertyNamePrefix)); + $originalPropertyName = substr($fieldName, strlen($propertyNamePrefix)); } - if (!$originalPropertyName) { + if (null === $originalPropertyName) { return $fieldName; } diff --git a/src/Controller/DefaultController.php b/src/Controller/DefaultController.php index c89b49ae..9db5e7a0 100644 --- a/src/Controller/DefaultController.php +++ b/src/Controller/DefaultController.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2023 Contao Community Alliance. + * (c) 2013-2024 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -25,7 +25,7 @@ * @author Sven Baumann * @author Ingolf Steinhardt * @author Tim Gatzky - * @copyright 2013-2023 Contao Community Alliance. + * @copyright 2013-2024 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -315,7 +315,7 @@ protected function assembleChildrenFor(ModelInterface $model, $sortingProperty = } $config->setFilter($condition->getFilter($model)); - if ($sortingProperty) { + if (null !== $sortingProperty) { $config->setSorting([$sortingProperty => 'ASC']); } @@ -612,7 +612,7 @@ private function getActionsFromSource(ModelIdInterface $source, ModelIdInterface $filter = new Filter(); $filter->andModelIsFromProvider($dataProvider); - if ($basicDefinition->getParentDataProvider()) { + if (null !== $basicDefinition->getParentDataProvider()) { $parentDataProvider = $basicDefinition->getDataProvider(); assert(\is_string($parentDataProvider)); @@ -831,7 +831,7 @@ private function ensureSameGrouping(array $actions, ModelIdInterface $after = nu { $environment = $this->getEnvironment(); $groupingMode = ViewHelpers::getGroupingMode($environment); - if ($groupingMode && $after && $after->getId()) { + if (null !== $groupingMode && null !== $after && $after->getId()) { // when pasting after another item, inherit the grouping field $groupingField = $groupingMode['property']; $previous = $this->modelCollector->getModel($after); @@ -982,7 +982,7 @@ private function processPasteTopAfterModel(CollectionInterface $models, ModelIdI if ($parent && $models->count()) { $manualSorting = ViewHelpers::getManualSortingProperty($this->getEnvironment()); - if ($manualSorting) { + if (null !== $manualSorting) { $this->pasteTop($models, $manualSorting, $parent); return; diff --git a/src/Controller/SortingManager.php b/src/Controller/SortingManager.php index 8763b3c0..120cdd39 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-2023 Contao Community Alliance. + * (c) 2013-2024 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-2023 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2024 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -108,7 +109,7 @@ public function __construct( $this->setSiblings($siblings); } - if ($sortedBy) { + if (null !== $sortedBy) { $this->setSortingProperty($sortedBy); } diff --git a/src/Controller/TreeCollector.php b/src/Controller/TreeCollector.php index 434026db..716f6151 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-2023 Contao Community Alliance. + * (c) 2013-2024 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 Stefan Heimes * @author Sven Baumann * @author Ingolf Steinhardt - * @copyright 2013-2023 Contao Community Alliance. + * @copyright 2013-2024 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -36,6 +36,13 @@ use ContaoCommunityAlliance\DcGeneral\EnvironmentAwareInterface; use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; use ContaoCommunityAlliance\DcGeneral\Panel\PanelContainerInterface; +use RuntimeException; + +use function array_merge; +use function count; +use function is_array; +use function is_string; +use function sprintf; /** * Generic class to retrieve a tree collection for tree views. @@ -49,28 +56,28 @@ class TreeCollector implements EnvironmentAwareInterface * * @var EnvironmentInterface */ - private $environment; + private EnvironmentInterface $environment; /** * The panel container in use. * * @var PanelContainerInterface */ - private $panel; + private PanelContainerInterface $panel; /** * The sorting information. * * @var array */ - private $sorting; + private array $sorting; /** * The tree node states that represent the current flags for the tree nodes. * * @var TreeNodeStates */ - private $states; + private TreeNodeStates $states; /** * Create a new instance. @@ -185,9 +192,9 @@ private function getChildrenOfModel( ->setFilter($childCondition->getFilter($model)) ->setIdOnly(true) ); - assert(\is_array($childIds)); + assert(is_array($childIds)); - if (!\count($childIds)) { + if (!count($childIds)) { return null; } @@ -264,7 +271,7 @@ private function treeWalkModel(ModelInterface $model, int $intLevel, array $subT } // If expanded, store children. - if ($model->getMeta($model::SHOW_CHILDREN) && \count($childCollections)) { + if ($model->getMeta($model::SHOW_CHILDREN) && count($childCollections)) { $model->setMeta($model::CHILD_COLLECTIONS, $childCollections); } @@ -279,7 +286,7 @@ private function treeWalkModel(ModelInterface $model, int $intLevel, array $subT * * @return void * - * @throws \RuntimeException When the parent provider does not match. + * @throws RuntimeException When the parent provider does not match. */ private function addParentFilter(ConfigInterface $config, ModelInterface $parentModel) { @@ -292,11 +299,11 @@ private function addParentFilter(ConfigInterface $config, ModelInterface $parent assert($basicDefinition instanceof BasicDefinitionInterface); $parentDataProvider = $basicDefinition->getParentDataProvider(); - assert(\is_string($parentDataProvider)); + assert(is_string($parentDataProvider)); if ($parentDataProvider !== $parentModel->getProviderName()) { - throw new \RuntimeException( - \sprintf( + throw new RuntimeException( + sprintf( 'Parent provider mismatch: %s vs. %s', $parentDataProvider, $parentModel->getProviderName() @@ -305,7 +312,7 @@ private function addParentFilter(ConfigInterface $config, ModelInterface $parent } $rootDataProvider = $basicDefinition->getRootDataProvider(); - assert(\is_string($rootDataProvider)); + assert(is_string($rootDataProvider)); // Apply parent filtering, do this only for root elements. if ( @@ -317,8 +324,8 @@ private function addParentFilter(ConfigInterface $config, ModelInterface $parent $baseFilter = $config->getFilter(); $filter = $parentCondition->getFilter($parentModel); - if ($baseFilter) { - $filter = \array_merge($baseFilter, $filter); + if (is_array($baseFilter)) { + $filter = array_merge($baseFilter, $filter); } $config->setFilter($filter); @@ -395,8 +402,8 @@ public function getChildrenOf($providerName, $level = 0, $parentModel = null) $baseFilter = $rootConfig->getFilter(); $filter = $rootCondition->getFilterArray(); - if ($baseFilter) { - $filter = \array_merge($baseFilter, $filter); + if (is_array($baseFilter)) { + $filter = array_merge($baseFilter, $filter); } $rootConfig->setFilter($filter); diff --git a/src/Data/DefaultDataProvider.php b/src/Data/DefaultDataProvider.php index eaa8bd34..c6ffc603 100644 --- a/src/Data/DefaultDataProvider.php +++ b/src/Data/DefaultDataProvider.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2023 Contao Community Alliance. + * (c) 2013-2024 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -25,7 +25,7 @@ * @author Richard Henkenjohann * @author Alex Wuttke * @author Ingolf Steinhardt - * @copyright 2013-2023 Contao Community Alliance. + * @copyright 2013-2024 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -634,7 +634,7 @@ private function convertModelToDataPropertyArray(ModelInterface $model, int $tim } } - if ($this->timeStampProperty) { + if (null !== $this->timeStampProperty) { $data[$this->source . '.' . ($this->getTimeStampProperty() ?? '')] = $timestamp ?: \time(); } diff --git a/src/DataDefinition/Definition/View/BackCommand.php b/src/DataDefinition/Definition/View/BackCommand.php index e64d0468..1d950d2a 100644 --- a/src/DataDefinition/Definition/View/BackCommand.php +++ b/src/DataDefinition/Definition/View/BackCommand.php @@ -31,7 +31,7 @@ class BackCommand extends Command public function __construct() { parent::__construct(); - $this->extra['class'] = 'header_back'; + $this->extra['class'] = 'header_back dcg'; $this->extra['accesskey'] = 'b'; $this->extra['attributes'] = 'onclick="Backend.getScrollOffset();"'; $this diff --git a/src/DataDefinition/Definition/View/CreateModelCommand.php b/src/DataDefinition/Definition/View/CreateModelCommand.php index f66c6f5f..9fad2c4f 100644 --- a/src/DataDefinition/Definition/View/CreateModelCommand.php +++ b/src/DataDefinition/Definition/View/CreateModelCommand.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2024 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-2024 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -36,8 +37,8 @@ public function __construct() $this->extra['attributes'] = 'onclick="Backend.getScrollOffset();"'; $this ->setName('button_new') - ->setLabel('new.0') - ->setDescription('new.1'); + ->setLabel('new.label') + ->setDescription('new.description'); $this->parameters['act'] = 'create'; } } diff --git a/src/DataDefinition/Palette/Builder/PaletteBuilder.php b/src/DataDefinition/Palette/Builder/PaletteBuilder.php index df1fd138..b1291ca0 100644 --- a/src/DataDefinition/Palette/Builder/PaletteBuilder.php +++ b/src/DataDefinition/Palette/Builder/PaletteBuilder.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2023 Contao Community Alliance. + * (c) 2013-2024 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 Sven Baumann * @author Richard Henkenjohann * @author Ingolf Steinhardt - * @copyright 2013-2023 Contao Community Alliance. + * @copyright 2013-2024 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -740,7 +740,7 @@ public function createPalette($name = null) $palette = $this->paletteClass->newInstance(); - if ($name) { + if (null !== $name) { $palette->setName($name); } @@ -843,7 +843,7 @@ public function finishLegend(&$legend = null) throw new DcGeneralRuntimeException('Legend is missing, please create a legend first'); } - if ($this->property) { + if (null !== $this->property) { $this->finishProperty(); } @@ -874,7 +874,7 @@ public function finishLegend(&$legend = null) */ public function useProperty($property, $_ = null) { - if ($this->property) { + if (null !== $this->property) { $this->finishProperty(); } @@ -910,7 +910,7 @@ public function useProperty($property, $_ = null) */ public function createProperty($propertyName, $_ = null) { - if ($this->property) { + if (null !== $this->property) { $this->finishProperty(); } @@ -945,7 +945,7 @@ public function createProperty($propertyName, $_ = null) */ public function finishProperty(&$property = null) { - if (!$this->property) { + if (null === $this->property) { throw new DcGeneralRuntimeException('Property is missing, please create a property first'); } @@ -1120,7 +1120,7 @@ public function createPropertyValueCondition($propertyName, $propertyValue, $str $this->finishCondition(); } - if ($this->property) { + if (null !== $this->property) { $condition = $this->propertyValueConditionClass->newInstance(); } elseif ($this->palette) { $condition = $this->palettePropertyValueConditionClass->newInstance(); @@ -1167,7 +1167,7 @@ public function chainPropertyValueCondition( $strict = false, $conjunction = PropertyConditionChain::AND_CONJUNCTION ) { - if ($this->property) { + if (null !== $this->property) { $this->createPropertyConditionChain($conjunction); $condition = $this->propertyValueConditionClass->newInstance(); } elseif ($this->palette) { diff --git a/src/DataDefinition/Palette/Condition/Palette/PropertyTrueCondition.php b/src/DataDefinition/Palette/Condition/Palette/PropertyTrueCondition.php index d9865b62..fcb271e0 100644 --- a/src/DataDefinition/Palette/Condition/Palette/PropertyTrueCondition.php +++ b/src/DataDefinition/Palette/Condition/Palette/PropertyTrueCondition.php @@ -46,6 +46,10 @@ public function getMatchCount(ModelInterface $model = null, PropertyValueBag $in return false; } - return ($this->strict ? (true === $value) : $value) ? $this->getWeight() : false; + if ($this->strict) { + return (true === $value) ? $this->getWeight() : false; + } + + return ((bool) $value) ? $this->getWeight() : false; } } diff --git a/src/DataDefinition/Palette/Palette.php b/src/DataDefinition/Palette/Palette.php index bb559645..67edab7e 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-2023 Contao Community Alliance. + * (c) 2013-2024 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-2023 Contao Community Alliance. + * @copyright 2013-2024 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ diff --git a/src/DataDefinition/Palette/PaletteInterface.php b/src/DataDefinition/Palette/PaletteInterface.php index c3be360f..2a467325 100644 --- a/src/DataDefinition/Palette/PaletteInterface.php +++ b/src/DataDefinition/Palette/PaletteInterface.php @@ -98,7 +98,7 @@ public function clearLegends(); /** * Set all legends to this palette. * - * @param array|LegendInterface[] $legends The legends. + * @param list $legends The legends. * * @return PaletteInterface */ @@ -107,8 +107,8 @@ public function setLegends(array $legends); /** * Add all legends to this palette. * - * @param array|LegendInterface[] $legends The legends. - * @param LegendInterface $before The legend before which the new legends shall be inserted (optional). + * @param list $legends The legends. + * @param LegendInterface $before The legend before which the new legends shall be inserted (optional). * * @return PaletteInterface */ @@ -165,7 +165,7 @@ public function getLegend($name); /** * Return the legends from this palette. * - * @return array|LegendInterface[] + * @return list */ public function getLegends(); diff --git a/src/EventListener/StoreRefererListener.php b/src/EventListener/StoreRefererListener.php new file mode 100644 index 00000000..9ffa87cc --- /dev/null +++ b/src/EventListener/StoreRefererListener.php @@ -0,0 +1,142 @@ + + * @author Ingolf Steinhardt + * @copyright 2013-2024 Contao Community Alliance. + * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later + * @filesource + */ + +declare(strict_types=1); + +namespace ContaoCommunityAlliance\DcGeneral\EventListener; + +use Contao\CoreBundle\Routing\ScopeMatcher; +use Contao\User; +use RuntimeException; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpKernel\Event\ResponseEvent; +use Symfony\Component\Security\Core\Security; + +use function array_merge; +use function array_shift; +use function count; +use function is_array; +use function strlen; + +/** + * @internal + */ +class StoreRefererListener +{ + public function __construct( + private readonly Security $security, + private readonly ScopeMatcher $scopeMatcher + ) { + } + + /** + * Stores the referer in the session. + * + * @SuppressWarnings(PHPMD.CyclomaticComplexity) + * @SuppressWarnings(PHPMD.NPathComplexity) + */ + public function __invoke(ResponseEvent $event): void + { + if (!$this->scopeMatcher->isBackendMainRequest($event)) { + return; + } + + $request = $event->getRequest(); + if (!$request->isMethod(Request::METHOD_GET)) { + return; + } + + $response = $event->getResponse(); + if (200 !== $response->getStatusCode()) { + return; + } + + $user = $this->security->getUser(); + if (!$user instanceof User) { + return; + } + + if (!$this->canModifyBackendSession($request)) { + return; + } + + if (!$request->hasSession()) { + throw new RuntimeException('The request did not contain a session.'); + } + + $session = $request->getSession(); + $key = $request->query->has('popup') ? 'popupReferer' : 'referer'; + $refererId = $request->attributes->get('_contao_referer_id'); + $referers = $this->prepareBackendReferer($refererId, $session->get($key)); + $ref = (string) $request->query->get('ref', ''); + + // Move current to last if the referer is in both the URL and the session. + if ('' !== $ref && isset($referers[$ref])) { + $referers[$refererId] = array_merge($referers[$ref], $referers[$refererId]); + $referers[$refererId]['last'] = $referers[$ref]['current']; + } + + // Set new current referer + $referers[$refererId]['current'] = $this->getRelativeRequestUri($request); + + $session->set($key, $referers); + } + + private function canModifyBackendSession(Request $request): bool + { + return true === $request->attributes->get('_dcg_referer_update') && !$request->isXmlHttpRequest(); + } + + /** + * @param string $refererId + * @param ?array> $referers + * + * @return array> + */ + private function prepareBackendReferer(string $refererId, ?array $referers = null): array + { + if (!is_array($referers)) { + $referers = []; + } + + if (!isset($referers[$refererId])) { + $last = end($referers); + if ([] === $last || false === $last) { + $last = ['last' => '']; + } + $referers[$refererId] = $last; + } + + // Make sure we never have more than 25 different referer URLs + while (count($referers) >= 25) { + array_shift($referers); + } + + return $referers; + } + + /** + * Returns the current request URI relative to the base path. + */ + private function getRelativeRequestUri(Request $request): string + { + return substr($request->getRequestUri(), strlen($request->getBasePath()) + 1); + } +} diff --git a/src/Resources/config/backend_event_subscribers.yml b/src/Resources/config/backend_event_subscribers.yml index 2be544df..0a591669 100644 --- a/src/Resources/config/backend_event_subscribers.yml +++ b/src/Resources/config/backend_event_subscribers.yml @@ -36,6 +36,7 @@ services: public: true arguments: - "@cca.dc-general.scope-matcher" + - "@security.helper" tags: - name: kernel.event_subscriber diff --git a/src/Resources/config/contao/handler_backend_listeners.yml b/src/Resources/config/contao/handler_backend_listeners.yml index 7d812069..bc6055d3 100644 --- a/src/Resources/config/contao/handler_backend_listeners.yml +++ b/src/Resources/config/contao/handler_backend_listeners.yml @@ -27,6 +27,8 @@ services: arguments: - "@cca.dc-general.scope-matcher" - "@cca.dc-general.security-url-builder-factory" + - "@request_stack" + - "@router" tags: - name: kernel.event_listener event: dc-general.action diff --git a/src/Resources/config/event_listeners.yml b/src/Resources/config/event_listeners.yml index 55fb9e8b..763a19c9 100644 --- a/src/Resources/config/event_listeners.yml +++ b/src/Resources/config/event_listeners.yml @@ -61,3 +61,10 @@ services: - name: kernel.event_listener event: dc-general.model.post-paste priority: -256 + + ContaoCommunityAlliance\DcGeneral\EventListener\StoreRefererListener: + arguments: + - '@security.helper' + - '@contao.routing.scope_matcher' + tags: + - kernel.event_listener diff --git a/src/Resources/contao/templates/dcbe_general_clipboard.html5 b/src/Resources/contao/templates/dcbe_general_clipboard.html5 index 63f01dd7..31c8c05e 100644 --- a/src/Resources/contao/templates/dcbe_general_clipboard.html5 +++ b/src/Resources/contao/templates/dcbe_general_clipboard.html5 @@ -52,7 +52,7 @@ if (\count($this->options)): ?>
    options as $id => $row): ?> -
  • +
  • getAction()] ?> diff --git a/src/Resources/contao/templates/dcbe_general_common_list.html5 b/src/Resources/contao/templates/dcbe_general_common_list.html5 index 95a3275e..202c4b20 100644 --- a/src/Resources/contao/templates/dcbe_general_common_list.html5 +++ b/src/Resources/contao/templates/dcbe_general_common_list.html5 @@ -11,7 +11,7 @@ $translator = \System::getContainer()->get('translator'); assert($translator instanceof TranslatorInterface); ?> block('messages'); ?> -getMessages(); ?> +getMessages() ?> info): ?>

    ', $this->info) ?>

    @@ -25,7 +25,7 @@ assert($translator instanceof TranslatorInterface); -
    +
    breadcrumb ?> select) : ?> subHeadline) : ?> @@ -40,13 +40,13 @@ assert($translator instanceof TranslatorInterface); header)) : ?>
    - headerButtons; ?> + headerButtons ?>
    header as $key => $value) : ?> - - + +
    @@ -92,7 +92,7 @@ assert($translator instanceof TranslatorInterface); getMeta($model::GROUP_VALUE); ?> -
    +
    @@ -100,7 +100,7 @@ assert($translator instanceof TranslatorInterface); getMeta($model::LABEL_VALUE) as $label) : ?> colspan=""> - + select) : ?> @@ -109,7 +109,7 @@ assert($translator instanceof TranslatorInterface); - getMeta($model::OPERATION_BUTTONS); ?> + getMeta($model::OPERATION_BUTTONS) ?> sortable) : ?> generateImage('system/themes/' . $this->theme . '/icons/drag.svg', '⇅', 'class="drag"') ?> @@ -134,7 +134,7 @@ assert($translator instanceof TranslatorInterface); select) : ?>
    floatRightSelectButtons): ?> style="text-align:right;">
    - selectButtons); ?> + selectButtons) ?>
    diff --git a/src/Resources/contao/templates/dcbe_general_show.html5 b/src/Resources/contao/templates/dcbe_general_show.html5 index 636205c5..af7fd12a 100644 --- a/src/Resources/contao/templates/dcbe_general_show.html5 +++ b/src/Resources/contao/templates/dcbe_general_show.html5 @@ -15,7 +15,7 @@ assert($translator instanceof \Symfony\Contracts\Translation\TranslatorInterface ?> diff --git a/src/Resources/public/css/generalDriver.css b/src/Resources/public/css/generalDriver.css index bbb2f6eb..74e3ef63 100644 --- a/src/Resources/public/css/generalDriver.css +++ b/src/Resources/public/css/generalDriver.css @@ -1,2 +1,2 @@ @charset "UTF-8";@media projection,screen{div table.tl_listing{margin-top:0!important;margin-bottom:0!important}.tl_folder_tlist{border-top:0}.treepicker_popup a.folding{float:left;margin-right:20px}.tl_listing tr.tl_folder_clipped>td{background-color:#fff9e6}.cursor_disabled{cursor:not-allowed}#dcg_clipboard{position:relative;padding-top:2px;padding-left:40px;min-height:32px;background-image:url(../images/clipboard.svg);background-size:24px 24px;background-position:6px 3px;background-repeat:no-repeat}#dcg_clipboard .btn-clear{position:absolute;top:3px;left:6px;opacity:0;-webkit-transition:opacity 300ms;transition:opacity 300ms}#dcg_clipboard .btn-clear:hover{opacity:1;-webkit-transition:opacity 300ms;transition:opacity 300ms}#dcg_clipboard ul{display:block}#dcg_clipboard li{display:inline-block;border:1px solid #ddd;background-color:#f9f9f9;padding:2px 4px}#dcg_clipboard li>*{display:inline-block;vertical-align:middle}#dcg_clipboard img{vertical-align:middle}.tl_language_panel{padding:6px 6px 7px 0;background:#f3f3f3;border-top:1px solid #fff;border-bottom:1px solid #bbb;text-align:right}.tl_language_panel .tl_select{width:240px}.mac .tl_language_panel select{max-width:none}.mac .tl_language_panel .tl_select,.mac .tl_panel_bottom .tl_select{font-size:11px}.ie .tl_language_panel,.safari .tl_language_panel{overflow:hidden}.tl_language_panel .tl_formbody{position:relative}.ie .tl_language_panel .tl_formbody,.safari .tl_language_panel .tl_formbody{float:right}.tl_language_panel .tl_submit{margin-top:3px;vertical-align:top}.ie .tl_language_panel .tl_submit,.webkit .tl_language_panel .tl_submit{padding-top:4px;padding-bottom:4px}.opera .tl_language_panel .tl_submit{padding-top:3px;padding-bottom:3px;margin-top:-1px}.tl_language_panel img{position:relative;top:4px;vertical-align:top}#general_messageBox{width:-webkit-min-content;width:-moz-min-content;width:min-content;max-width:60%;min-width:40%;padding:24px;position:absolute;left:0;right:0;margin:0 auto;background:#fff no-repeat right center;border:2px solid #666;border-radius:6px;font-family:"Trebuchet MS",Verdana,sans-serif;font-size:15px;text-align:left;word-wrap:break-word}#general_messageBox.loading{background-image:url(../images/loading.gif);padding-right:60px}#general_messageBox.box-small{width:-webkit-max-content;width:-moz-max-content;width:max-content}#general_messageBox .tl_submit{text-align:center}#general_messageBox .tl_submit_container{text-align:right}#general_messageBox .tl_submit_container [type=submit]{margin-left:4px}.stickySave #general_messageBox .tl_submit_container{text-align:right;position:relative}.stickySave #general_messageBox .tl_submit_container [type=submit]{margin-left:4px}#general_messageOverlay{width:-webkit-max-content;width:-moz-max-content;width:max-content;height:100%;position:absolute;top:0;left:0;background:#fff;opacity:0.5}.header_stop{background-image:url("/system/themes/flexible/icons/stop.svg");background-size:16px}} -/*# sourceMappingURL=generalDriver.css.map */ +/*# sourceMappingURL=generalDriver.css.map */ \ No newline at end of file diff --git a/src/Resources/public/css/generalDriver.css.map b/src/Resources/public/css/generalDriver.css.map index 298a3c53..404a8114 100644 --- a/src/Resources/public/css/generalDriver.css.map +++ b/src/Resources/public/css/generalDriver.css.map @@ -1 +1 @@ -{"version":3,"sources":["generalDriver.css","../sass/generalDriver.scss","../sass/_clipboard.scss","../sass/_languagePanel.scss","../sass/_messageBox.scss","../sass/_headerButton.scss"],"names":[],"mappings":"AAAA,gBAAgB,CCUhB,yBACE,qBACE,sBAAA,CACA,yBDCF,CCEA,iBACE,YDCF,CCEA,4BACE,UAAA,CACA,iBDCF,CCEA,oCACE,wBDCF,CCEA,iBACE,kBDCF,CE/BF,eACE,iBAAA,CACA,eAAA,CACA,iBAAA,CACA,eAAA,CACA,6CAAA,CACA,yBAAA,CACA,2BAAA,CACA,2BFkCA,CEhCA,0BACE,iBAAA,CACA,OAAA,CACA,QAAA,CACA,SAAA,CACA,gCAAA,CAAA,wBFkCF,CEhCE,gCACE,SAAA,CACA,gCAAA,CAAA,wBFkCJ,CE/BA,kBACE,aFiCF,CE/BA,kBACE,oBAAA,CACA,qBAAA,CACA,wBAAA,CACA,eFiCF,CE/BE,oBACE,oBAAA,CACA,qBFiCJ,CE9BA,mBACE,qBFgCF,CGrEF,mBACE,qBAAA,CACA,kBAAA,CACA,yBAAA,CACA,4BAAA,CACA,gBHwEA,CGrEF,8BACE,WHwEA,CGrEF,+BACE,cHwEA,CGrEF,oEAEE,cHwEA,CGrEF,kDAEE,eHwEA,CGrEF,gCACE,iBHwEA,CGrEF,4EAEE,WHwEA,CGrEF,8BACE,cAAA,CACA,kBHwEA,CGrEF,wEAEE,eAAA,CACA,kBHwEA,CGrEF,qCACE,eAAA,CACA,kBAAA,CACA,eHwEA,CGrEF,uBACE,iBAAA,CACA,OAAA,CACA,kBHwEA,CI/HF,oBACE,yBAAA,CAAA,sBAAA,CAAA,iBAAA,CACA,aAAA,CACA,aAAA,CACA,YAAA,CACA,iBAAA,CACA,MAAA,CACA,OAAA,CACA,aAAA,CACA,sCAAA,CACA,qBAAA,CACA,iBAAA,CACA,6CAAA,CACA,cAAA,CACA,eAAA,CACA,oBJkIA,CIhIA,4BACE,2CAAA,CACA,kBJkIF,CI/HA,8BACE,yBAAA,CAAA,sBAAA,CAAA,iBJiIF,CI9HA,+BACE,iBJgIF,CI7HA,yCACE,gBJ+HF,CI7HE,uDACE,eJ+HJ,CIzHF,qDACE,gBAAA,CACA,iBJ6HA,CI3HA,mEACE,eJ6HF,CIzHF,wBACE,yBAAA,CAAA,sBAAA,CAAA,iBAAA,CACA,WAAA,CACA,iBAAA,CACA,KAAA,CACA,MAAA,CACA,eAAA,CACA,WJ4HA,CKpLF,aACE,8DAAA,CACA,oBLuLA,CACF","file":"generalDriver.css"} \ No newline at end of file +{"version":3,"sources":["generalDriver.css","../sass/generalDriver.scss","../sass/_clipboard.scss","../sass/_languagePanel.scss","../sass/_messageBox.scss","../sass/_headerButton.scss"],"names":[],"mappings":"AAAA,gBAAgB,CCUhB,yBACE,qBACE,sBAAA,CACA,yBDCF,CCEA,iBACE,YDAF,CCGA,4BACE,UAAA,CACA,iBDDF,CCIA,oCACE,wBDFF,CCKA,iBACE,kBDHF,CE3BF,eACE,iBAAA,CACA,eAAA,CACA,iBAAA,CACA,eAAA,CACA,6CAAA,CACA,yBAAA,CACA,2BAAA,CACA,2BF6BA,CE3BA,0BACE,iBAAA,CACA,OAAA,CACA,QAAA,CACA,SAAA,CACA,gCAAA,CAAA,wBF6BF,CE3BE,gCACE,SAAA,CACA,gCAAA,CAAA,wBF6BJ,CE1BA,kBACE,aF4BF,CE1BA,kBACE,oBAAA,CACA,qBAAA,CACA,wBAAA,CACA,eF4BF,CE1BE,oBACE,oBAAA,CACA,qBF4BJ,CEzBA,mBACE,qBF2BF,CGhEF,mBACE,qBAAA,CACA,kBAAA,CACA,yBAAA,CACA,4BAAA,CACA,gBHkEA,CG/DF,8BACE,WHiEA,CG9DF,+BACE,cHgEA,CG7DF,oEAEE,cH+DA,CG5DF,kDAEE,eH8DA,CG3DF,gCACE,iBH6DA,CG1DF,4EAEE,WH4DA,CGzDF,8BACE,cAAA,CACA,kBH2DA,CGxDF,wEAEE,eAAA,CACA,kBH0DA,CGvDF,qCACE,eAAA,CACA,kBAAA,CACA,eHyDA,CGtDF,uBACE,iBAAA,CACA,OAAA,CACA,kBHwDA,CI/GF,oBACE,yBAAA,CAAA,sBAAA,CAAA,iBAAA,CACA,aAAA,CACA,aAAA,CACA,YAAA,CACA,iBAAA,CACA,MAAA,CACA,OAAA,CACA,aAAA,CACA,sCAAA,CACA,qBAAA,CACA,iBAAA,CACA,6CAAA,CACA,cAAA,CACA,eAAA,CACA,oBJiHA,CI/GA,4BACE,2CAAA,CACA,kBJiHF,CI9GA,8BACE,yBAAA,CAAA,sBAAA,CAAA,iBJgHF,CI7GA,+BACE,iBJ+GF,CI5GA,yCACE,gBJ8GF,CI5GE,uDACE,eJ8GJ,CIxGF,qDACE,gBAAA,CACA,iBJ2GA,CIzGA,mEACE,eJ2GF,CIvGF,wBACE,yBAAA,CAAA,sBAAA,CAAA,iBAAA,CACA,WAAA,CACA,iBAAA,CACA,KAAA,CACA,MAAA,CACA,eAAA,CACA,WJyGA,CKjKF,aACE,8DAAA,CACA,oBLmKA,CACF","file":"generalDriver.css"} \ No newline at end of file diff --git a/src/Resources/public/js/vanillaGeneral.js b/src/Resources/public/js/vanillaGeneral.js index e311ad0c..d920677c 100644 --- a/src/Resources/public/js/vanillaGeneral.js +++ b/src/Resources/public/js/vanillaGeneral.js @@ -157,7 +157,7 @@ function GeneralTableDnD() // Build url. var href = window.location.href.replace(/\?.*$/, ''); - var req = window.location.search; + var req = window.location.search + '?'; req += '&act=paste'; req += '&source=' + id; req += '&after=' + insertAfter; diff --git a/src/Resources/public/sass/generalDriver.scss b/src/Resources/public/sass/generalDriver.scss index b0197935..ded539c3 100644 --- a/src/Resources/public/sass/generalDriver.scss +++ b/src/Resources/public/sass/generalDriver.scss @@ -26,10 +26,10 @@ .tl_listing tr.tl_folder_clipped > td { background-color: #fff9e6; } - + .cursor_disabled{ cursor: not-allowed; - } + } @import "clipboard"; @import "languagePanel"; diff --git a/src/Resources/translations/dc-general.de.xlf b/src/Resources/translations/dc-general.de.xlf index d35d683b..fe2c7e12 100644 --- a/src/Resources/translations/dc-general.de.xlf +++ b/src/Resources/translations/dc-general.de.xlf @@ -1,459 +1,471 @@ - - - - Language - Sprache - - - - - Switch language - Sprache wechseln - - - - - Table: %table% - Tabelle: %table% - - - - - The property "%property%" is already in the database and not unique. - Die Eigenschaft "%property%" ist schon in der Datenbank und nicht unique. - - - - - Nothing was selected! - Es wurde nichts ausgewählt! - - - - - Ok - Ok - - - - - Abort - Abbrechen - - - - - Cancel - Beenden - - - - - Cancel multiple processing. - Die Mehrfachbearbeitung beenden. - - - - - The property "%property%" isn't supported in mode "%mode%". - Die Eigenschaft "%property%" wird im Modus "%mode%" nicht unterstützt. - - - - - You must select the property "%property%" for edit it. - Die Eigenschaft "%property%" muss ausgewählt sein, damit diese bearbeitet werden kann. - - - - - You must select the parent property "%parent_property%" for edit the property - "%edit_property%". - - Die Abhängige Eigenschaft "%parent_property%" muss ausgewählt sein, damit man die Eigenschaft - "%edit_property%" bearbeiten kann. - - - - - - Select properties - Eigenschaften auswählen - - - - - Please check your selected properties! - Bitte überprüfen Sie die ausgewählten Eigenschaften! - - - - - Select models - Elemente auswählen - - - - - Disabled: %title% - Gesperrt: %title% - - - - - Create a new item - Neuen Datensatz erstellen - - - - - Edit record %id% - Datensatz %id% bearbeiten - - - - - W Y - W. Y - - - - - Delete - Löschen - - - - - Edit - Bearbeiten - - - - - Override - Überschreiben - - - - - Copy - Kopieren - - - - - Close - Schließen - - - - - Continue - Weiter - - - - - Do you really want to delete the selected records? - Wollen Sie die ausgewählten Einträge wirklich löschen? - - - - - Delete - Löschen - - - - - Move - Verschieben - - - - - Copy - Kopieren - - - - - Edit - Bearbeiten - - - - - Override - Überschreiben - - - - - Edit multiple - Mehrere bearbeiten - - - - - no - nein - - - - - yes - ja - - - - - Show the details of record %id% - Details des Datensatzes %id% anzeigen - - - - - Color picker - Farbe auswählen - - - - - Word wrap - Zeilenumbruch - - - - - Open the help wizard - Den Hilfe-Assistent aufrufen - - - - - Change selection - Auswahl ändern - - - - - Drag the items to re-order them - Elemente können durch Ziehen umsortiert werden - - - - - Reset selection - Auswahl aufheben - - - - - Select all - Alle auswählen - - - - - Collapse node - Bereich schließen - - - - - Expand node - Bereich öffnen - - - - - Save - Speichern - - - - - Save and close - Speichern und schließen - - - - - Save and edit - Speichern und bearbeiten - - - - - Save and go back - Speichern und zurück - - - - - Save and new - Speichern und neu - - - - - Save and duplicate - Speichern und duplizieren - - - - - Go back - Zurück - - - - - Back to the previous page - Zurück zur vorherigen Seite - - - - - Revision date - Änderungsdatum - - - - - 0 - 1 - - - - - %B %d%o, %Y - %d. %B %Y - - - - - ID - ID - - - - - Parent ID - Elternelement - - - - - Sorting value - Sortierindex - - - - - Search - Suchen - - - - - Treepicker manager - Treepicker-Manager - - - - - utf-8 - utf-8 - - - - - Records - Datensätze - - - - - All - Alle - - - - - Update mode - Update-Modus - - - - - Add the selected values - Ausgewählte Werte hinzufügen - - - - - Remove the selected values - Ausgewählte Werte entfernen - - - - - Replace the existing entries - Bestehende Einträge überschreiben - - - - - Apply - Anwenden - - - - - Sort - Sortieren - - - - - Show - Anzeigen - - - - - Reset - Zurücksetzen - - - - - Version - Version - - - - - Restore - Wiederherstellen - - - - - Clear clipboard - Ablage leeren - - - - - No records found. - Keine Einträge gefunden. - - - + + + + Language + Sprache + + + + + Switch language + Sprache wechseln + + + + + Table: %table% + Tabelle: %table% + + + + + Color picker + Farbe auswählen + + + + + Page picker + Seite auswählen + + + + + The property "%property%" is already in the database and not unique. + Die Eigenschaft "%property%" ist schon in der Datenbank und nicht unique. + + + + + Nothing was selected! + Es wurde nichts ausgewählt! + + + + + Ok + Ok + + + + + Abort + Abbrechen + + + + + Cancel + Beenden + + + + + Cancel multiple processing. + Die Mehrfachbearbeitung beenden. + + + + + The property "%property%" isn't supported in mode "%mode%". + Die Eigenschaft "%property%" wird im Modus "%mode%" nicht unterstützt. + + + + + You must select the property "%property%" for edit it. + Die Eigenschaft "%property%" muss ausgewählt sein, damit diese bearbeitet werden kann. + + + + + You must select the parent property "%parent_property%" for edit the property + "%edit_property%". + + Die Abhängige Eigenschaft "%parent_property%" muss ausgewählt sein, damit man die Eigenschaft + "%edit_property%" bearbeiten kann. + + + + + + Select properties + Eigenschaften auswählen + + + + + Please check your selected properties! + Bitte überprüfen Sie die ausgewählten Eigenschaften! + + + + + Select models + Elemente auswählen + + + + + Disabled: %title% + Gesperrt: %title% + + + + + Create a new item + Neuen Datensatz erstellen + + + + + Edit record %id% + Datensatz %id% bearbeiten + + + + + W Y + W. Y + + + + + Delete + Löschen + + + + + Edit + Bearbeiten + + + + + Override + Überschreiben + + + + + Copy + Kopieren + + + + + Close + Schließen + + + + + Continue + Weiter + + + + + Do you really want to delete the selected records? + Wollen Sie die ausgewählten Einträge wirklich löschen? + + + + + Delete + Löschen + + + + + Move + Verschieben + + + + + Copy + Kopieren + + + + + Edit + Bearbeiten + + + + + Override + Überschreiben + + + + + Edit multiple + Mehrere bearbeiten + + + + + no + nein + + + + + yes + ja + + + + + Show the details of record %id% + Details des Datensatzes %id% anzeigen + + + + + Word wrap + Zeilenumbruch + + + + + Open the help wizard + Den Hilfe-Assistent aufrufen + + + + + Change selection + Auswahl ändern + + + + + Drag the items to re-order them + Elemente können durch Ziehen umsortiert werden + + + + + Reset selection + Auswahl aufheben + + + + + Select all + Alle auswählen + + + + + Collapse node + Bereich schließen + + + + + Expand node + Bereich öffnen + + + + + Save + Speichern + + + + + Save and close + Speichern und schließen + + + + + Save and edit + Speichern und bearbeiten + + + + + Save and go back + Speichern und zurück + + + + + Save and new + Speichern und neu + + + + + Save and duplicate + Speichern und duplizieren + + + + + Go back + Zurück + + + + + Back to the previous page + Zurück zur vorherigen Seite + + + + + Revision date + Änderungsdatum + + + + + 0 + 1 + + + + + %B %d%o, %Y + %d. %B %Y + + + + + ID + ID + + + + + Parent ID + Elternelement + + + + + Sorting value + Sortierindex + + + + + Search + Suchen + + + + + Treepicker manager + Treepicker-Manager + + + + + utf-8 + utf-8 + + + + + Records + Datensätze + + + + + All + Alle + + + + + Update mode + Update-Modus + + + + + Add the selected values + Ausgewählte Werte hinzufügen + + + + + Remove the selected values + Ausgewählte Werte entfernen + + + + + Replace the existing entries + Bestehende Einträge überschreiben + + + + + Apply + Anwenden + + + + + Sort + Sortieren + + + + + Show + Anzeigen + + + + + Reset + Zurücksetzen + + + + + Version + Version + + + + + Restore + Wiederherstellen + + + + + Clear clipboard + Ablage leeren + + + + + Remove this item from clipboard + Element von Ablage entfernen + + + + + No records found. + Keine Einträge gefunden. + + + diff --git a/src/Resources/translations/dc-general.en.xlf b/src/Resources/translations/dc-general.en.xlf index d80a62c4..c475f774 100644 --- a/src/Resources/translations/dc-general.en.xlf +++ b/src/Resources/translations/dc-general.en.xlf @@ -1,382 +1,391 @@ - - - - Language - - - - - Switch language - - - - - Table: %table% - - - - - The property "%property%" is already in the database and not unique. - - - - - Nothing was selected! - - - - - Ok - - - - - Abort - - - - - Cancel - - - - - Cancel multiple processing. - - - - - The property "%property%" isn't supported in mode "%mode%". - - - - - You must select the property "%property%" for edit it. - - - - - You must select the parent property "%parent_property%" for edit the property - "%edit_property%". - - - - - - Select properties - - - - - Please check your selected properties! - - - - - Select models - - - - - Disabled: %title% - - - - - Create a new item - - - - - Edit record %id% - - - - - W Y - - - - - Delete - - - - - Edit - - - - - Override - - - - - Copy - - - - - Close - - - - - Continue - - - - - Do you really want to delete the selected records? - - - - - Delete - - - - - Move - - - - - Copy - - - - - Edit - - - - - Override - - - - - Edit multiple - - - - - no - - - - - yes - - - - - Show the details of record %id% - - - - - Color picker - - - - - Word wrap - - - - - Open the help wizard - - - - - Change selection - - - - - Drag the items to re-order them - - - - - Reset selection - - - - - Select all - - - - - Collapse node - - - - - Expand node - - - - - Save - - - - - Save and close - - - - - Save and edit - - - - - Save and go back - - - - - Save and new - - - - - Save and duplicate - - - - - Go back - - - - - Back to the previous page - - - - - Revision date - - - - - 0 - - - - - %B %d%o, %Y - - - - - ID - - - - - Parent ID - - - - - Sorting value - - - - - Search - - - - - Treepicker manager - - - - - utf-8 - - - - - Records - - - - - All - - - - - Update mode - - - - - Add the selected values - - - - - Remove the selected values - - - - - Replace the existing entries - - - - - Apply - - - - - Sort - - - - - Show - - - - - Reset - - - - - Version - - - - - Restore - - - - - Clear clipboard - - - - - No records found. - - - + + + + Language + + + + + Switch language + + + + + Table: %table% + + + + + Color picker + + + + + Page picker + + + + + The property "%property%" is already in the database and not unique. + + + + + Nothing was selected! + + + + + Ok + + + + + Abort + + + + + Cancel + + + + + Cancel multiple processing. + + + + + The property "%property%" isn't supported in mode "%mode%". + + + + + You must select the property "%property%" to edit it. + + + + + You must select the parent property "%parent_property%" to edit the property "%edit_property%". + + + + + + Select properties + + + + + Please check your selected properties! + + + + + Select models + + + + + Disabled: %title% + + + + + Create a new item + + + + + Edit record %id% + + + + + W Y + + + + + Delete + + + + + Edit + + + + + Override + + + + + Copy + + + + + Close + + + + + Continue + + + + + Do you really want to delete the selected records? + + + + + Delete + + + + + Move + + + + + Copy + + + + + Edit + + + + + Override + + + + + Edit multiple + + + + + no + + + + + yes + + + + + Show the details of record %id% + + + + + Word wrap + + + + + Open the help wizard + + + + + Change selection + + + + + Drag the items to re-order them + + + + + Reset selection + + + + + Select all + + + + + Collapse node + + + + + Expand node + + + + + Save + + + + + Save and close + + + + + Save and edit + + + + + Save and go back + + + + + Save and new + + + + + Save and duplicate + + + + + Go back + + + + + Back to the previous page + + + + + Revision date + + + + + 0 + + + + + %B %d%o, %Y + + + + + ID + + + + + Parent ID + + + + + Sorting value + + + + + Search + + + + + Treepicker manager + + + + + utf-8 + + + + + Records + + + + + All + + + + + Update mode + + + + + Add the selected values + + + + + Remove the selected values + + + + + Replace the existing entries + + + + + Apply + + + + + Sort + + + + + Show + + + + + Reset + + + + + Version + + + + + Restore + + + + + Clear clipboard + + + + + Remove this item from clipboard + + + + + No records found. + + + diff --git a/src/View/ActionHandler/AbstractPropertyVisibilityHandler.php b/src/View/ActionHandler/AbstractPropertyVisibilityHandler.php index 5ac0e156..75661c01 100644 --- a/src/View/ActionHandler/AbstractPropertyVisibilityHandler.php +++ b/src/View/ActionHandler/AbstractPropertyVisibilityHandler.php @@ -22,6 +22,7 @@ use ContaoCommunityAlliance\DcGeneral\Action; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\GetPropertyOptionsEvent; +use ContaoCommunityAlliance\DcGeneral\Data\PropertyValueBag; use ContaoCommunityAlliance\DcGeneral\DataDefinition; use ContaoCommunityAlliance\DcGeneral\DataDefinition\ConditionChainInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\ConditionInterface; @@ -253,7 +254,11 @@ protected function ensurePropertyVisibleInModel( continue; } - $invisible = $property->getVisibleCondition()->match($model, $propertyValues, $property, $legend); + $visibilityCondition = $property->getVisibleCondition(); + assert($visibilityCondition instanceof PropertyConditionInterface); + // This is sadly needed, as the match function only accepts a concrete instance instead of an interface. + assert($propertyValues instanceof PropertyValueBag); + $invisible = $visibilityCondition->match($model, $propertyValues, $property, $legend); } } @@ -415,6 +420,8 @@ private function invisiblePaletteProperty( * @param EnvironmentInterface $environment The environment. * * @return null|string + * + * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ #[ReturnTypeWillChange] protected function injectSelectParentPropertyInformation( @@ -438,12 +445,12 @@ protected function injectSelectParentPropertyInformation( if ($property->getName() !== $legendProperty->getName()) { continue; } + $visibleCondition = $legendProperty->getVisibleCondition(); + if (null === $visibleCondition) { + continue; + } - $this->matchInvisibleProperty( - $legendProperty->getVisibleCondition(), - $invisibleProperties, - $environment - ); + $this->matchInvisibleProperty($visibleCondition, $invisibleProperties, $environment); } } @@ -498,7 +505,10 @@ protected function injectSelectSubPropertiesInformation( $information = []; foreach ($properties as $propertyName => $informationProperty) { - $label = !$informationProperty->getLabel() ? $propertyName : $informationProperty->getLabel(); + $label = $translator->translate( + $informationProperty->getLabel() ?: $propertyName, + $environment->getDataDefinition()?->getName() + ); $information[] = '

    ' . @@ -613,6 +623,8 @@ private function matchInvisibleSubProperties( $palettesDefinition = $this->getDataDefinition($environment)->getPalettesDefinition(); $testPropertyValueBag = clone $propertyValueBag; + // This is sadly needed, as the match function only accepts a concrete instance instead of an interface. + assert($testPropertyValueBag instanceof PropertyValueBag); $testPropertyValueBag->setPropertyValue('dummyNotVisible', true); $palette = $palettesDefinition->findPalette($model); @@ -634,6 +646,9 @@ private function matchInvisibleSubProperties( } $conditions = $legendProperty->getVisibleCondition(); + if (null === $conditions) { + continue; + } if (!$conditions->match($model, $testPropertyValueBag, $legendProperty, $legend)) { continue; diff --git a/tests/Contao/View/Contao2BackendView/Subscriber/CheckPermissionTest.php b/tests/Contao/View/Contao2BackendView/Subscriber/CheckPermissionTest.php index ce28e156..ba7e8715 100644 --- a/tests/Contao/View/Contao2BackendView/Subscriber/CheckPermissionTest.php +++ b/tests/Contao/View/Contao2BackendView/Subscriber/CheckPermissionTest.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2024 Contao Community Alliance. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,13 +13,15 @@ * @package contao-community-alliance/dc-general * @author Sven Baumann * @author Christian Schiffler - * @copyright 2013-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2024 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ namespace ContaoCommunityAlliance\DcGeneral\Test\Contao\View\Contao2BackendView\Subscriber; +use Contao\CoreBundle\Security\ContaoCorePermissions; use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminator; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Subscriber\CheckPermission; use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; @@ -33,7 +35,8 @@ use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Property; use ContaoCommunityAlliance\DcGeneral\Factory\Event\BuildDataDefinitionEvent; use ContaoCommunityAlliance\DcGeneral\Test\TestCase; -use PHPUnit_Framework_MockObject_MockObject; +use PHPUnit\Framework\MockObject\MockObject; +use Symfony\Component\Security\Core\Security; /** * This tests the CheckPermission subscriber. @@ -65,6 +68,8 @@ public function testGetSubscribedEvents() * * @covers \ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Subscriber\CheckPermission::checkPermissionForProperties() * @covers \ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Subscriber\CheckPermission::getVisibilityConditionChain() + * + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ public function testCheckPermissionForProperties() { @@ -123,14 +128,31 @@ function ($name) { $event = new BuildDataDefinitionEvent($container); - /** @var RequestScopeDeterminator|PHPUnit_Framework_MockObject_MockObject $determinator */ + /** @var RequestScopeDeterminator|MockObject $determinator */ $determinator = $this ->getMockBuilder(RequestScopeDeterminator::class) - ->setMethods(['currentScopeIsBackend']) + ->onlyMethods(['currentScopeIsBackend']) + ->disableOriginalConstructor() + ->getMock(); + /** @var Security|MockObject $determinator */ + $security = $this + ->getMockBuilder(Security::class) + ->onlyMethods(['isGranted']) ->disableOriginalConstructor() ->getMock(); - $subscriber = new CheckPermission($determinator); + $subscriber = new CheckPermission($determinator, $security); $determinator->expects(self::once())->method('currentScopeIsBackend')->willReturn(true); + $security + ->expects(self::exactly(3)) + ->method('isGranted') + ->willReturnCallback(function (string $permission, string $fieldName): bool { + static $invocation = 0; + $fields = ['property11', 'property21', 'property22']; + self::assertSame(ContaoCorePermissions::USER_CAN_EDIT_FIELD_OF_TABLE, $permission); + self::assertSame('::' . $fields[$invocation++], $fieldName); + + return false; + }); $subscriber->checkPermissionForProperties($event); self::assertInstanceOf(PropertyConditionChain::class, $property11->getVisibleCondition()); From 5ace0015bbd2d3b4ca2ecd876a5a0e5110d08e52 Mon Sep 17 00:00:00 2001 From: Ingolf Steinardt Date: Wed, 15 May 2024 17:00:50 +0200 Subject: [PATCH 4/5] Update github action versions --- .github/workflows/diagnostics.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/diagnostics.yml b/.github/workflows/diagnostics.yml index bddb943b..36e23651 100644 --- a/.github/workflows/diagnostics.yml +++ b/.github/workflows/diagnostics.yml @@ -19,7 +19,7 @@ jobs: steps: - name: Pull source - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup PHP with PECL extension uses: shivammathur/setup-php@v2 @@ -28,7 +28,7 @@ jobs: # setup caches - name: Cache composer cache directory - uses: actions/cache@v3 + uses: actions/cache@v4 env: cache-name: composer-cache-dir with: @@ -36,7 +36,7 @@ jobs: key: ${{ runner.os }}-${{ matrix.php }}-build-${{ env.cache-name }} - name: Cache vendor directory - uses: actions/cache@v3 + uses: actions/cache@v4 env: cache-name: vendor with: @@ -46,7 +46,7 @@ jobs: ${{ runner.os }}-${{ matrix.php }}-${{ matrix.contao }}-build-${{ env.cache-name }}- - name: Cache phpcq directory - uses: actions/cache@v3 + uses: actions/cache@v4 env: cache-name: phpcq with: @@ -68,7 +68,7 @@ jobs: run: ./vendor/bin/phpcq run -v ${{ matrix.output }} - name: Upload build directory to artifact - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 if: ${{ success() }} || ${{ failure() }} with: name: phpcq-builds-php-${{ matrix.php }}-${{ matrix.contao }} From 08c3d580bf866017bbad18191d435bcd7fa1ab67 Mon Sep 17 00:00:00 2001 From: Ingolf Steinardt Date: Wed, 15 May 2024 17:46:53 +0200 Subject: [PATCH 5/5] Fix PHPCQ --- src/Clipboard/Clipboard.php | 11 ++++++----- .../MultipleHandler/OverrideAllHandler.php | 2 +- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/Clipboard/Clipboard.php b/src/Clipboard/Clipboard.php index f3d733c0..79b5e20b 100644 --- a/src/Clipboard/Clipboard.php +++ b/src/Clipboard/Clipboard.php @@ -3,7 +3,7 @@ /** * This file is part of contao-community-alliance/dc-general. * - * (c) 2013-2019 Contao Community Alliance. + * (c) 2013-2024 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-2019 Contao Community Alliance. + * @author Ingolf Steinhardt + * @copyright 2013-2024 Contao Community Alliance. * @license https://github.com/contao-community-alliance/dc-general/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -129,8 +130,8 @@ public function remove(ItemInterface $item) public function removeById(ModelIdInterface $modelId) { $serializedId = $modelId->getSerialized(); - if (!empty($this->itemsByModelId[$serializedId])) { - foreach ($this->itemsByModelId[$serializedId] as $item) { + if ([] !== ($items = $this->itemsByModelId[$serializedId] ?? [])) { + foreach ($items as $item) { unset($this->items[$item->getClipboardId()]); } @@ -176,7 +177,7 @@ public function has(ItemInterface $item) */ public function hasId(ModelIdInterface $modelId) { - return !empty($this->itemsByModelId[$modelId->getSerialized()]); + return (bool) ($this->itemsByModelId[$modelId->getSerialized()] ?? false); } /** diff --git a/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/OverrideAllHandler.php b/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/OverrideAllHandler.php index 2224e88e..a2d42b17 100644 --- a/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/OverrideAllHandler.php +++ b/src/Contao/View/Contao2BackendView/ActionHandler/MultipleHandler/OverrideAllHandler.php @@ -329,7 +329,7 @@ private function renderFieldSets( $translator = $environment->getTranslator(); assert($translator instanceof TranslatorInterface); - if (empty($fieldSet['palette'])) { + if ('' !== $fieldSet['palette']) { $fieldSet['palette'] = \sprintf( '

     

    %s

     

    ', $translator->translate('no_properties_available', 'dc-general')