From 4db8f95735017c79483b4fb28d362e32ce470b15 Mon Sep 17 00:00:00 2001 From: Christian Schiffler Date: Mon, 15 Jul 2024 21:31:46 +0200 Subject: [PATCH 1/2] Support passing url reference type in filter url builder --- .gitignore | 4 ++- composer.json | 2 +- .../Table/RenderSettings/JumpToListener.php | 36 +++++++++++++++++++ .../dca/tl_metamodel_rendersettings.php | 14 ++++++-- src/CoreBundle/Resources/public/css/style.css | 26 ++++++++++++++ .../Resources/public/scss/style.scss | 32 +++++++++++++++-- .../tl_metamodel_rendersettings.en.xlf | 18 ++++++++++ src/Filter/FilterUrlBuilder.php | 9 ++--- src/Render/Setting/Collection.php | 14 ++++++-- src/Render/Setting/ICollection.php | 5 +-- 10 files changed, 146 insertions(+), 14 deletions(-) diff --git a/.gitignore b/.gitignore index 6561ee7c1..e27324c56 100644 --- a/.gitignore +++ b/.gitignore @@ -16,5 +16,7 @@ vendor/ composer.lock # build -.phpunit.result.cache +/.cache +/.phpunit.result.cache +/.pdepend/* /.phpcq/* diff --git a/composer.json b/composer.json index 49a488ea1..c07af13b8 100644 --- a/composer.json +++ b/composer.json @@ -32,7 +32,7 @@ "require": { "php": "^8.1", "ext-dom": "*", - "contao-community-alliance/dc-general": "^2.3.10", + "contao-community-alliance/dc-general": "^2.3.15", "contao-community-alliance/events-contao-bindings": "^4.13.1", "contao-community-alliance/meta-palettes": "^2.0.10", "contao-community-alliance/translator": "^2.4.2", diff --git a/src/CoreBundle/EventListener/DcGeneral/Table/RenderSettings/JumpToListener.php b/src/CoreBundle/EventListener/DcGeneral/Table/RenderSettings/JumpToListener.php index 0efaac7be..22393f63a 100644 --- a/src/CoreBundle/EventListener/DcGeneral/Table/RenderSettings/JumpToListener.php +++ b/src/CoreBundle/EventListener/DcGeneral/Table/RenderSettings/JumpToListener.php @@ -31,6 +31,7 @@ use Doctrine\DBAL\Connection; use MetaModels\IFactory; use MetaModels\IMetaModel; +use Symfony\Component\Routing\Generator\UrlGeneratorInterface; use Symfony\Contracts\Translation\TranslatorInterface; /** @@ -38,6 +39,22 @@ */ class JumpToListener extends AbstractAbstainingListener { + private const DEFAULT_TYPE = UrlGeneratorInterface::ABSOLUTE_PATH; + + private const TYPE_MAP = [ + 'absolute_url' => UrlGeneratorInterface::ABSOLUTE_URL, + 'absolute_path' => UrlGeneratorInterface::ABSOLUTE_PATH, + 'relative_path' => UrlGeneratorInterface::RELATIVE_PATH, + 'network_path' => UrlGeneratorInterface::NETWORK_PATH, + ]; + + private const TYPE_MAP_INVERSE = [ + UrlGeneratorInterface::ABSOLUTE_URL => 'absolute_url', + UrlGeneratorInterface::ABSOLUTE_PATH => 'absolute_path', + UrlGeneratorInterface::RELATIVE_PATH => 'relative_path', + UrlGeneratorInterface::NETWORK_PATH => 'network_path', + ]; + /** * The MetaModel factory. * @@ -105,6 +122,7 @@ public function decodeValue(DecodePropertyValueForWidgetEvent $event) foreach (\array_keys($languages) as $key) { $newValue = ''; $filter = 0; + $type = self::TYPE_MAP_INVERSE[self::DEFAULT_TYPE]; if ($value) { foreach ($value as $arr) { if (!\is_array($arr)) { @@ -114,6 +132,7 @@ public function decodeValue(DecodePropertyValueForWidgetEvent $event) // Set the new value and exit the loop. if (\in_array($key, $arr, true)) { $newValue = '{{link_url::' . $arr['value'] . '}}'; + $type = self::TYPE_MAP_INVERSE[$arr['type'] ?? self::DEFAULT_TYPE]; $filter = $arr['filter']; break; } @@ -123,6 +142,7 @@ public function decodeValue(DecodePropertyValueForWidgetEvent $event) // Build the new array. $newValues[] = [ 'langcode' => $key, + 'type' => $type, 'value' => $newValue, 'filter' => $filter ]; @@ -148,6 +168,7 @@ public function encodeValue(EncodePropertyValueFromWidgetEvent $event) foreach ($value as $k => $v) { $value[$k]['value'] = \str_replace(['{{link_url::', '}}'], ['', ''], $v['value']); + $value[$k]['type'] = self::TYPE_MAP[$v['type']] ?? self::DEFAULT_TYPE; } $event->setValue(\serialize($value)); @@ -209,12 +230,27 @@ public function buildWidget(BuildWidgetEvent $event) ]; } + $extra['columnFields']['type']['options'] = $this->getUrlTypes(); $extra['columnFields']['filter']['options'] = $this->getFilterSettings($model); $event->getProperty()->setExtra($extra); } + private function getUrlTypes(): array + { + $result = []; + foreach (self::TYPE_MAP_INVERSE as $typeName) { + $result[$typeName] = $this->translator->trans( + 'jumpTo_type.' . $typeName, + [], + 'tl_metamodel_rendersettings' + ); + } + + return $result; + } + /** * Retrieve the model filters for the MCW. * diff --git a/src/CoreBundle/Resources/contao/dca/tl_metamodel_rendersettings.php b/src/CoreBundle/Resources/contao/dca/tl_metamodel_rendersettings.php index 636114838..c7c8a9055 100644 --- a/src/CoreBundle/Resources/contao/dca/tl_metamodel_rendersettings.php +++ b/src/CoreBundle/Resources/contao/dca/tl_metamodel_rendersettings.php @@ -290,16 +290,26 @@ 'exclude' => true, 'inputType' => 'justtextoption', 'eval' => [ + 'tl_class' => 'jumpTo_language', 'valign' => 'center' ] ], + 'type' => [ + 'label' => 'jumpTo_type.label', + 'description' => 'jumpTo_type.description', + 'exclude' => true, + 'inputType' => 'select', + 'eval' => [ + 'tl_class' => 'jumpTo_type', + ] + ], 'value' => [ 'label' => 'jumpTo_page.label', 'description' => 'jumpTo_page.description', 'exclude' => true, 'inputType' => 'text', 'eval' => [ - 'style' => 'width:90%;' + 'tl_class' => 'jumpTo_page', ] ], 'filter' => [ @@ -308,7 +318,7 @@ 'exclude' => true, 'inputType' => 'select', 'eval' => [ - 'style' => 'width:100%;', + 'tl_class' => 'jumpTo_filter', 'includeBlankOption' => true, 'chosen' => true ] diff --git a/src/CoreBundle/Resources/public/css/style.css b/src/CoreBundle/Resources/public/css/style.css index 34c266dd2..1c8c22695 100644 --- a/src/CoreBundle/Resources/public/css/style.css +++ b/src/CoreBundle/Resources/public/css/style.css @@ -15,6 +15,7 @@ * @author Sven Baumann * @author Carolina Koehn * @author Cliff Parnitzky + * @author Christian Schiffler * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource @@ -164,3 +165,28 @@ form[id^=mm_] .sort_hint { .tl_formbody_edit .settings { margin: 0 0 10px 12px; } + +#tl_metamodel_rendersettings .jumpTo_language { + width: 15%; +} +#tl_metamodel_rendersettings .jumpTo_type { + width: 15%; +} +#tl_metamodel_rendersettings .jumpTo_type select { + width: 100%; +} +#tl_metamodel_rendersettings .jumpTo_page { + width: 15%; +} +#tl_metamodel_rendersettings .jumpTo_page input { + width: 88%; +} +#tl_metamodel_rendersettings .jumpTo_filter { + width: 55%; +} +#tl_metamodel_rendersettings .jumpTo_filter select { + width: 100%; +} +#tl_metamodel_rendersettings .operations { + display: none; +} diff --git a/src/CoreBundle/Resources/public/scss/style.scss b/src/CoreBundle/Resources/public/scss/style.scss index 8eb1c77c3..855618291 100644 --- a/src/CoreBundle/Resources/public/scss/style.scss +++ b/src/CoreBundle/Resources/public/scss/style.scss @@ -1,7 +1,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2022 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Ingolf Steinhardt * @author Sven Baumann * @author Carolina Koehn - * @copyright 2012-2022 The MetaModels team. + * @author Christian Schiffler + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -144,3 +145,30 @@ form[id^=mm_] .sort_hint { .tl_formbody_edit .settings { margin: 0 0 10px 12px; } + +#tl_metamodel_rendersettings { + .jumpTo_language { + width: 15%; + } + .jumpTo_type { + width: 15%; + select { + width: 100%; + } + } + .jumpTo_page { + width: 15%; + input { + width: 88%; + } + } + .jumpTo_filter { + width: 55%; + select { + width: 100%; + } + } + .operations { + display: none; + } +} diff --git a/src/CoreBundle/Resources/translations/tl_metamodel_rendersettings.en.xlf b/src/CoreBundle/Resources/translations/tl_metamodel_rendersettings.en.xlf index 9649e9518..5147cad20 100644 --- a/src/CoreBundle/Resources/translations/tl_metamodel_rendersettings.en.xlf +++ b/src/CoreBundle/Resources/translations/tl_metamodel_rendersettings.en.xlf @@ -107,6 +107,24 @@ The language for the jump to page. + + URL type + + + The reference type to use when generating the URL. + + + Absolute URL, e.g. "https://example.com/dir/file". + + + Absolute path, e.g. "/dir/file". + + + Relative path based on the current request path, e.g. "../parent-file". + + + Network path, e.g. "//example.com/dir/file". + Jump to page diff --git a/src/Filter/FilterUrlBuilder.php b/src/Filter/FilterUrlBuilder.php index 2b4573bd5..1ed92821c 100644 --- a/src/Filter/FilterUrlBuilder.php +++ b/src/Filter/FilterUrlBuilder.php @@ -122,14 +122,15 @@ public function __construct( /** * Generate a frontend url. * - * @param FilterUrl $filterUrl The filter URL. + * @param FilterUrl $filterUrl The filter URL. + * @param int $referenceType The url reference type. * * @return string * * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.Superglobals) */ - public function generate(FilterUrl $filterUrl): string + public function generate(FilterUrl $filterUrl, int $referenceType = UrlGeneratorInterface::ABSOLUTE_PATH): string { $jumpTo = $filterUrl->getPage(); @@ -172,11 +173,11 @@ public function generate(FilterUrl $filterUrl): string } if ($this->hasLegacyRouting) { - return $this->urlGenerator->generate($jumpTo['alias'] . $url, $parameters); + return $this->urlGenerator->generate($jumpTo['alias'] . $url, $parameters, $referenceType); } $parameters['parameters'] = $url; - return $this->urlGenerator->generate('tl_page.' . $jumpTo['id'], $parameters); + return $this->urlGenerator->generate('tl_page.' . $jumpTo['id'], $parameters, $referenceType); } /** diff --git a/src/Render/Setting/Collection.php b/src/Render/Setting/Collection.php index 13fe9e2cd..12a90a6f4 100644 --- a/src/Render/Setting/Collection.php +++ b/src/Render/Setting/Collection.php @@ -35,10 +35,13 @@ use MetaModels\IMetaModel; use MetaModels\ITranslatedMetaModel; use Symfony\Component\EventDispatcher\EventDispatcherInterface; +use Symfony\Component\Routing\Generator\UrlGeneratorInterface; use Symfony\Contracts\Translation\TranslatorInterface; /** * Base implementation for render settings. + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class Collection implements ICollection { @@ -278,12 +281,14 @@ private function lookupJumpTo(bool $translated, string $desired = null, string $ { $jumpToPageId = ''; $filterSettingId = ''; + $referenceType = UrlGeneratorInterface::ABSOLUTE_PATH; foreach ((array) $this->get('jumpTo') as $jumpTo) { $langCode = $jumpTo['langcode'] ?? null; // If either desired language or fallback, keep the result. if (!$translated || ($langCode === $desired) || ($langCode === $fallback)) { $jumpToPageId = $jumpTo['value'] ?? ''; $filterSettingId = (string) ($jumpTo['filter'] ?? ''); + $referenceType = (int) ($jumpTo['type'] ?? UrlGeneratorInterface::ABSOLUTE_PATH); // If the desired language, break. // Otherwise, try to get the desired one until all have been evaluated. if (!$translated || ($desired === $jumpTo['langcode'])) { @@ -302,6 +307,7 @@ private function lookupJumpTo(bool $translated, string $desired = null, string $ 'pageDetails' => $pageDetails, 'filter' => $filterSettingId, 'filterSetting' => $filterSetting, + 'referenceType' => $referenceType, // Mask out the "all languages" language key (See #687). 'language' => $pageDetails['language'] ?? '', 'label' => $this->getJumpToLabel() @@ -311,7 +317,7 @@ private function lookupJumpTo(bool $translated, string $desired = null, string $ /** * {@inheritdoc} */ - public function buildJumpToUrlFor(IItem $item) + public function buildJumpToUrlFor(IItem $item /**, int $referenceType */) { $information = $this->determineJumpToInformation(); if (empty($information['pageDetails'])) { @@ -341,7 +347,11 @@ public function buildJumpToUrlFor(IItem $item) $result['params'] = $parameterList; $result['deep'] = !empty($filterUrl->getSlugParameters()); - $result['url'] = $this->filterUrlBuilder->generate($filterUrl); + $result['url'] = $this->filterUrlBuilder->generate( + $filterUrl, + $information['referenceType'] + ?? ((1 < func_num_args()) ? (int) func_get_arg(1) : UrlGeneratorInterface::ABSOLUTE_PATH) + ); return $result; } diff --git a/src/Render/Setting/ICollection.php b/src/Render/Setting/ICollection.php index 94dfe37cb..562cad2d6 100644 --- a/src/Render/Setting/ICollection.php +++ b/src/Render/Setting/ICollection.php @@ -78,9 +78,10 @@ public function getSettingNames(); /** * Render a filter url for the given item. * - * @param IItem $item The item to generate the filter url for. + * @param IItem $item The item to generate the filter url for. + * @param int $referenceType Optional reference type - mandatory from MetaModels 3.0 on. * * @return array */ - public function buildJumpToUrlFor(IItem $item); + public function buildJumpToUrlFor(IItem $item /**, int $referenceType */); } From 36e4384f2cdd2563ea75efac47cf75ffbfab7827 Mon Sep 17 00:00:00 2001 From: Christian Schiffler Date: Mon, 15 Jul 2024 21:33:39 +0200 Subject: [PATCH 2/2] Fix psalm issue for deprecated interface --- src/IMetaModel.php | 4 ++-- src/MetaModel.php | 2 -- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/IMetaModel.php b/src/IMetaModel.php index de02bf631..b3e4d8622 100644 --- a/src/IMetaModel.php +++ b/src/IMetaModel.php @@ -35,6 +35,8 @@ * @see MetaModelFactory::byId To instantiate a MetaModel by its ID. * * @see IFactory::getMetaModel To instantiate a MetaModel by its table name. + * + * @psalm-suppress DeprecatedInterface */ interface IMetaModel { @@ -44,8 +46,6 @@ interface IMetaModel * @return IMetaModelsServiceContainer * * @deprecated Inject services via constructor or setter. - * - * @psalm-suppress DeprecatedInterface */ public function getServiceContainer(); diff --git a/src/MetaModel.php b/src/MetaModel.php index 74879038e..f6fc5f751 100644 --- a/src/MetaModel.php +++ b/src/MetaModel.php @@ -193,8 +193,6 @@ public function getServiceContainer() * @return MetaModel * * @deprecated Inject services via constructor or setter. - * - * @psalm-suppress DeprecatedInterface */ public function setServiceContainer($serviceContainer, $deprecationNotice = true) {