Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 26 additions & 14 deletions src/CoreBundle/Controller/ListControllerTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
/**
* This file is part of MetaModels/core.
*
* (c) 2012-2025 The MetaModels team.
* (c) 2012-2026 The MetaModels team.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
Expand All @@ -15,7 +15,7 @@
* @author Christian Schiffler <c.schiffler@cyberspectrum.de>
* @author Ingolf Steinhardt <info@e-spin.de>
* @author Sven Baumann <baumann.sv@gmail.com>
* @copyright 2012-2025 The MetaModels team.
* @copyright 2012-2026 The MetaModels team.
* @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later
* @filesource
*/
Expand All @@ -33,6 +33,7 @@
use MetaModels\Filter\FilterUrl;
use MetaModels\Filter\FilterUrlBuilder;
use MetaModels\Filter\Setting\IFilterSettingFactory;
use MetaModels\FrontendIntegration\FrontendFilterOptions;
use MetaModels\Helper\SortingLinkGenerator;
use MetaModels\IFactory;
use MetaModels\IItem;
Expand Down Expand Up @@ -341,9 +342,19 @@ private function getResponseInternal(Template $template, Model $model, Request $
*/
private function getFilterParameters(FilterUrl $filterUrl, ItemList $itemRenderer): array
{
$filterSetting = $itemRenderer->getFilterSettings();
/** @var array<string, string> $wantedByType */
$wantedByType = [];
// FIXME: improve this call - it does too much.
foreach (
$filterSetting->getParameterFilterWidgets([], [], new FrontendFilterOptions()) as $widgetName => $widget
) {
$wantedByType[$widgetName] = (string) ($widget['param_type'] ?? 'slugNget');
}

$result = [];
foreach ($itemRenderer->getFilterSettings()->getParameters() as $name) {
if (null !== $value = $this->tryReadFromSlugOrGet($filterUrl, $name, 'slugNget')) {
foreach ($filterSetting->getParameters() as $name) {
if (null !== $value = $this->tryReadFromSlugOrGet($filterUrl, $name, $wantedByType[$name] ?? 'slugNget')) {
$result[$name] = $value;
}
}
Expand All @@ -354,31 +365,32 @@ private function getFilterParameters(FilterUrl $filterUrl, ItemList $itemRendere
/**
* Get parameter from get or slug.
*
* @param FilterUrl $filterUrl The filter URL to obtain parameters from.
* @param string $sortParam The sort parameter name to obtain.
* @param string $sortType The sort URL type.
* @param FilterUrl $filterUrl The filter URL to obtain parameters from.
* @param string $name The parameter name to obtain.
* @param string $paramSource The source in the URL.
*
* @return string|null
*/
private function tryReadFromSlugOrGet(FilterUrl $filterUrl, string $sortParam, string $sortType): ?string
private function tryReadFromSlugOrGet(FilterUrl $filterUrl, string $name, string $paramSource): ?string
{
$result = null;

switch ($sortType) {
switch ($paramSource) {
case 'get':
$result = $filterUrl->getGet($sortParam);
$result = $filterUrl->getGet($name);
break;
case 'slug':
$result = $filterUrl->getSlug($sortParam);
$result = $filterUrl->getSlug($name);
break;
case 'slugNget':
$result = ($filterUrl->getGet($sortParam) ?? $filterUrl->getSlug($sortParam));
$result = ($filterUrl->getGet($name) ?? $filterUrl->getSlug($name));
break;
default:
}

// Mark the parameter as used (otherwise, a 404 is thrown)
Input::get($sortParam);
// Mark the parameter as used — InputEnhancer registers route parameters via setUnusedRouteParameters();
// FrontendTemplate::compile() throws UnusedArgumentsException (→ 404) for any that remain unconsumed.
Input::get($name);

return $result;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
<?php

/**
* This file is part of MetaModels/core.
*
* (c) 2012-2026 The MetaModels team.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* This project is provided in good faith and hope to be usable by anyone.
*
* @package MetaModels/core
* @author Ingolf Steinhardt <info@e-spin.de>
* @copyright 2012-2026 The MetaModels team.
* @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later
* @filesource
*/

namespace MetaModels\CoreBundle\EventListener\DcGeneral\Table\FilterSetting;

use Contao\Message;
use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminator;
use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\BuildWidgetEvent;
use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface;
use Symfony\Contracts\Translation\TranslatorInterface;

/**
* Displays a deprecation hint when a filter rule uses param_type "slugNget".
*
* The value "slugNget" is kept for backward compatibility with legacy MetaModels but will be removed in a future
* version. This listener informs the editor to switch to either "slug" or "get".
*
* The hint fires on BuildWidgetEvent (not PreEditModelEvent) so it only appears when the param_type field is
* actually visible in the active palette — i.e. for filter types that expose this setting.
*/
final class FilterSettingParamTypeHintListener
{
/**
* The scope determinator.
*
* @var RequestScopeDeterminator
*/
private RequestScopeDeterminator $scopeDeterminator;

/**
* The translator.
*
* @var TranslatorInterface
*/
private TranslatorInterface $translator;

/**
* Create a new instance.
*
* @param RequestScopeDeterminator $scopeDeterminator The scope determinator.
* @param TranslatorInterface $translator The translator.
*/
public function __construct(RequestScopeDeterminator $scopeDeterminator, TranslatorInterface $translator)
{
$this->scopeDeterminator = $scopeDeterminator;
$this->translator = $translator;
}

/**
* Add a deprecation hint when the param_type widget is rendered with value "slugNget".
*
* @param BuildWidgetEvent $event The event.
*
* @return void
*/
public function handle(BuildWidgetEvent $event): void
{
if (!$this->scopeDeterminator->currentScopeIsBackend()) {
return;
}

$dataDefinition = $event->getEnvironment()->getDataDefinition();
assert($dataDefinition instanceof ContainerInterface);

if ('tl_metamodel_filtersetting' !== $dataDefinition->getName()) {
return;
}

if ('param_type' !== $event->getProperty()->getName()) {
return;
}

if ('slugNget' !== $event->getModel()->getProperty('param_type')) {
return;
}

Message::addInfo($this->translator->trans('hint_param_type_slugNget', [], 'tl_metamodel_filtersetting'));
}
}
110 changes: 110 additions & 0 deletions src/CoreBundle/Migration/FilterSettingParamTypeMigration.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
<?php

/**
* This file is part of MetaModels/core.
*
* (c) 2012-2026 The MetaModels team.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* This project is provided in good faith and hope to be usable by anyone.
*
* @package MetaModels/core
* @author Ingolf Steinhardt <info@e-spin.de>
* @copyright 2012-2026 The MetaModels team.
* @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later
* @filesource
*/

declare(strict_types=1);

namespace MetaModels\CoreBundle\Migration;

use Contao\CoreBundle\Migration\AbstractMigration;
use Contao\CoreBundle\Migration\MigrationResult;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Exception;

use function array_keys;
use function in_array;

/**
* Adds the column `param_type` to `tl_metamodel_filtersetting` if it does not yet exist and pre-fills all existing
* rows with `slugNget`.
*
* Background: the DCA default for new filter rules is `slug`. Existing (legacy) filter rules stored no value for this
* column and must therefore receive `slugNget` so their behaviour stays unchanged after the column is introduced.
*/
final class FilterSettingParamTypeMigration extends AbstractMigration
{
private const TABLE = 'tl_metamodel_filtersetting';
private const COLUMN = 'param_type';

/**
* The database connection.
*
* @var Connection
*/
private Connection $connection;

/**
* Create a new instance.
*
* @param Connection $connection The database connection.
*/
public function __construct(Connection $connection)
{
$this->connection = $connection;
}

/**
* {@inheritDoc}
*/
#[\Override]
public function getName(): string
{
return 'Add column param_type to tl_metamodel_filtersetting and pre-fill legacy rows with slugNget.';
}

/**
* {@inheritDoc}
*
* @throws Exception
*/
#[\Override]
public function shouldRun(): bool
{
$schemaManager = $this->connection->createSchemaManager();
if (!$schemaManager->tablesExist([self::TABLE])) {
return false;
}

$columns = array_keys($schemaManager->listTableColumns(self::TABLE));

return !in_array(self::COLUMN, $columns, true);
}

/**
* {@inheritDoc}
*
* @throws Exception
*/
#[\Override]
public function run(): MigrationResult
{
$this->connection->executeStatement(
"ALTER TABLE `" . self::TABLE . "`
ADD COLUMN `" . self::COLUMN . "` varchar(10) NOT NULL default 'slug'"
);

$this->connection->executeStatement(
"UPDATE `" . self::TABLE . "` SET `" . self::COLUMN . "` = 'slugNget'"
);

return new MigrationResult(
true,
'Added column ' . self::TABLE . '.' . self::COLUMN . ' and pre-filled existing rows with slugNget.'
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -97,3 +97,12 @@ services:
tags:
- name: kernel.event_listener
event: 'dc-general.factory.build-data-definition'

MetaModels\CoreBundle\EventListener\DcGeneral\Table\FilterSetting\FilterSettingParamTypeHintListener:
arguments:
- '@cca.dc-general.scope-matcher'
- '@translator'
tags:
- name: kernel.event_listener
event: dc-general.view.contao2backend.build-widget
method: handle
6 changes: 6 additions & 0 deletions src/CoreBundle/Resources/config/services.yml
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,12 @@ services:
tags:
- name: contao.migration

MetaModels\CoreBundle\Migration\FilterSettingParamTypeMigration:
arguments:
$connection: '@database_connection'
tags:
- name: contao.migration

MetaModels\CoreBundle\Migration\JumpToMigration:
arguments:
$connection: '@database_connection'
Expand Down
17 changes: 17 additions & 0 deletions src/CoreBundle/Resources/contao/dca/tl_metamodel_filtersetting.php
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,7 @@
],
'+fefilter' => [
'urlparam',
'param_type',
'predef_param',
'fe_widget',
'allow_empty',
Expand Down Expand Up @@ -404,6 +405,22 @@
],
'sql' => "varchar(255) NOT NULL default ''"
],
'param_type' => [
'label' => 'param_type.label',
'description' => 'param_type.description',
'exclude' => true,
'inputType' => 'select',
'options' => ['slug', 'get', 'slugNget'],
'reference' => [
'slug' => 'param_type_options.slug',
'get' => 'param_type_options.get',
'slugNget' => 'param_type_options.slugNget',
],
'eval' => [
'tl_class' => 'w50',
],
'sql' => "varchar(10) NOT NULL default 'slug'",
],
'predef_param' => [
'label' => 'predef_param.label',
'description' => 'predef_param.description',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,30 @@
<source>URL parameter</source>
<target>URL-Parameter</target>
</trans-unit>
<trans-unit id="param_type.label" resname="param_type.label">
<source>URL type for parameter</source>
<target>URL-Typ für den Parameter</target>
</trans-unit>
<trans-unit id="param_type.description" resname="param_type.description">
<source>Define whether the filter parameter is placed as a URL path segment (slug) or as a query string parameter (GET).</source>
<target>Legt fest, ob der Filterparameter als URL-Pfadsegment (Slug) oder als Query-String-Parameter (GET) in die URL eingebaut wird.</target>
</trans-unit>
<trans-unit id="param_type_options.slug" resname="param_type_options.slug">
<source>Slug only</source>
<target>Nur Slug</target>
</trans-unit>
<trans-unit id="param_type_options.get" resname="param_type_options.get">
<source>GET only</source>
<target>Nur GET</target>
</trans-unit>
<trans-unit id="param_type_options.slugNget" resname="param_type_options.slugNget">
<source>Slug or GET allowed</source>
<target>Slug oder GET erlaubt - Deprecated: Slug ODER GET verwenden!</target>
</trans-unit>
<trans-unit id="hint_param_type_slugNget" resname="hint_param_type_slugNget">
<source>Set the URL type for the parameter to "Slug" OR "GET" - the "Slug or Get" setting will be removed in a future version.</source>
<target>URL-Typ für den Parameter auf "Slug" ODER "GET" einstellen - Einstellung "Slug oder Get" entfällt in künftiger Version.</target>
</trans-unit>
<trans-unit id="urlparam.description" resname="urlparam.description">
<source>The URL parameter that shall get mapped to the selected attribute. The special &lt;em&gt;&quot;auto_item&quot;&lt;/em&gt;
parameter can also be used, this is especially useful for alias columns.
Expand Down Expand Up @@ -613,4 +637,4 @@ wäre das Query: &quot;SELECT t.id FROM mm_demo AS t WHERE t.catname=\&apos;defa
</trans-unit>
</body>
</file>
</xliff>
</xliff>
Loading