From 77c1c416ea7fc0fb332af4aa5eea9251f4b7fa4e Mon Sep 17 00:00:00 2001 From: Jonatan Czarniecki Date: Fri, 6 Feb 2026 23:39:44 +0100 Subject: [PATCH 01/23] feat: add parameter type `multi-list` The new parameter type is similar to and shares logic with the `list` type. It accepts multiple values from a list of allowed values. The selected values are passed as array to the bridge. --- lib/BridgeCard.php | 6 ++++-- lib/ParameterValidator.php | 4 ++++ middlewares/SecurityMiddleware.php | 14 ++++++++------ 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/lib/BridgeCard.php b/lib/BridgeCard.php index 2f171002a44..130c3f6d73c 100644 --- a/lib/BridgeCard.php +++ b/lib/BridgeCard.php @@ -146,6 +146,8 @@ private static function renderForm( $form .= self::getNumberInput($inputEntry, $idArg, $id); } elseif ($inputEntry['type'] === 'list') { $form .= self::getListInput($inputEntry, $idArg, $id) . "\n"; + } elseif ($inputEntry['type'] === 'multi-list') { + $form .= self::getListInput($inputEntry, $idArg, $id, true) . "\n"; } elseif ($inputEntry['type'] === 'checkbox') { $form .= self::getCheckboxInput($inputEntry, $idArg, $id); } else { @@ -196,7 +198,7 @@ public static function getNumberInput(array $entry, string $id, string $name): s return sprintf('' . "\n", $attributes, $id, $defaultValue, $exampleValue, $name); } - public static function getListInput(array $entry, string $id, string $name): string + public static function getListInput(array $entry, string $id, string $name, bool $isMulti = false): string { $required = $entry['required'] ?? null; if ($required) { @@ -205,7 +207,7 @@ public static function getListInput(array $entry, string $id, string $name): str } $attributes = self::getInputAttributes($entry); - $list = sprintf('' . "\n", $attributes, $id, $name . ($isMulti ? '[]' : ''), $isMulti ? 'multiple ' : ''); foreach ($entry['values'] as $name => $value) { if (is_array($value)) { diff --git a/lib/ParameterValidator.php b/lib/ParameterValidator.php index e2783586210..488472c8487 100644 --- a/lib/ParameterValidator.php +++ b/lib/ParameterValidator.php @@ -31,6 +31,10 @@ public function validateInput(array &$input, $contexts): array case 'list': $input[$name] = $this->validateListValue($value, $contextParameters[$name]['values']); break; + case 'multi-list': + //TODO: Maybe move the array checking and looping through all of the values to another method? + $input[$name] = is_array($value) ? array_map(fn($v) => $this->validateListValue($v, $contextParameters[$name]['values']), $value) : null; + break; default: case 'text': if (isset($contextParameters[$name]['pattern'])) { diff --git a/middlewares/SecurityMiddleware.php b/middlewares/SecurityMiddleware.php index b07a814487a..a20af7b57d8 100644 --- a/middlewares/SecurityMiddleware.php +++ b/middlewares/SecurityMiddleware.php @@ -3,18 +3,20 @@ declare(strict_types=1); /** - * Make sure that only strings are allowed in GET parameters + * Make sure that only strings and arrays are allowed in GET parameters */ class SecurityMiddleware implements Middleware { public function __invoke(Request $request, $next): Response { foreach ($request->toArray() as $key => $value) { - if (!is_string($value)) { - return new Response(render(__DIR__ . '/../templates/error.html.php', [ - 'message' => "Query parameter \"$key\" is not a string.", - ]), 400); - } + //TODO: Maybe stricter checking for arrays? + // Not required technically because the values are properly checked + // in ParameterValidator, so basic check like this should be OK. + if (is_string($value) || (is_array($value) && !in_array(fn($v) => !is_string($v), $value))) continue; + return new Response(render(__DIR__ . '/../templates/error.html.php', [ + 'message' => "Query parameter \"$key\" is not a string.", + ]), 400); } return $next($request); } From a6cd0b6eed45e21c5088708d63b4fd032c9d54a2 Mon Sep 17 00:00:00 2001 From: Jonatan Czarniecki Date: Sat, 7 Feb 2026 00:31:25 +0100 Subject: [PATCH 02/23] fix: fix parameter validation for arrays * correctly search for invalid values and return error as soon as one is found * update the error message as well --- middlewares/SecurityMiddleware.php | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/middlewares/SecurityMiddleware.php b/middlewares/SecurityMiddleware.php index a20af7b57d8..e34f39220e2 100644 --- a/middlewares/SecurityMiddleware.php +++ b/middlewares/SecurityMiddleware.php @@ -10,12 +10,18 @@ class SecurityMiddleware implements Middleware public function __invoke(Request $request, $next): Response { foreach ($request->toArray() as $key => $value) { + if (is_string($value)) continue; //TODO: Maybe stricter checking for arrays? // Not required technically because the values are properly checked // in ParameterValidator, so basic check like this should be OK. - if (is_string($value) || (is_array($value) && !in_array(fn($v) => !is_string($v), $value))) continue; + if (is_array($value)) { + $flag = true; + foreach ($value as $v) + if (!($flag = is_string($v))) break; + if ($flag) continue; + } return new Response(render(__DIR__ . '/../templates/error.html.php', [ - 'message' => "Query parameter \"$key\" is not a string.", + 'message' => "Query parameter \"$key\" is not a string or array of strings.", ]), 400); } return $next($request); From c4f1880ac91970e73d9418b2b3fd346cfa754aa5 Mon Sep 17 00:00:00 2001 From: Jonatan Czarniecki Date: Sat, 7 Feb 2026 00:49:27 +0100 Subject: [PATCH 03/23] test: validate `BridgeCard::getListInput` method for `multi-list` --- tests/BridgeCardTest.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/BridgeCardTest.php b/tests/BridgeCardTest.php index 519ac7f19db..50268f08576 100644 --- a/tests/BridgeCardTest.php +++ b/tests/BridgeCardTest.php @@ -36,6 +36,7 @@ public function test() 'values' => [], ]; $this->assertSame('', BridgeCard::getListInput($entry, 'id', 'name')); + $this->assertSame('', BridgeCard::getListInput($entry, 'id', 'name', true)) $entry = [ 'defaultValue' => 2, @@ -44,6 +45,7 @@ public function test() ], ]; $this->assertSame('', BridgeCard::getListInput($entry, 'id', 'name')); + $this->assertSame('', BridgeCard::getListInput($entry, 'id', 'name', true)) // optgroup $entry = [ @@ -56,5 +58,9 @@ public function test() '', BridgeCard::getListInput($entry, 'id', 'name') ); + $this->assertSame( + '', + BridgeCard::getListInput($entry, 'id', 'name', true) + ); } } From d573a65a6378d3a4ae37825fc05df8f5926250e1 Mon Sep 17 00:00:00 2001 From: Jonatan Czarniecki Date: Sat, 7 Feb 2026 00:52:04 +0100 Subject: [PATCH 04/23] test: handle `multi-list` parameter type in `BridgeImplementationTest` --- tests/BridgeImplementationTest.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/BridgeImplementationTest.php b/tests/BridgeImplementationTest.php index dd68934edc9..01196dbcda4 100644 --- a/tests/BridgeImplementationTest.php +++ b/tests/BridgeImplementationTest.php @@ -70,6 +70,7 @@ public function testParameters($path) 'text', 'number', 'list', + 'multi-list', 'checkbox', ]; @@ -99,7 +100,7 @@ public function testParameters($path) $this->assertIsString($options['type'], $field . ': invalid type'); $this->assertContains($options['type'], $allowedTypes, $field . ': unknown type'); - if ($options['type'] == 'list') { + if ($options['type'] == 'list' || $options['type'] == 'multi-list') { $this->assertArrayHasKey('values', $options, $field . ': missing list values'); $this->assertIsArray($options['values'], $field . ': invalid list values'); $this->assertNotEmpty($options['values'], $field . ': empty list values'); @@ -116,6 +117,7 @@ public function testParameters($path) if ($options['required'] === true && isset($options['type'])) { switch ($options['type']) { case 'list': + case 'multi-list': case 'checkbox': $this->assertArrayNotHasKey( 'required', From b6cace48ef9bf8377f92c4ea69c31b869bdbf956 Mon Sep 17 00:00:00 2001 From: Jonatan Czarniecki Date: Sat, 7 Feb 2026 00:53:04 +0100 Subject: [PATCH 05/23] test: add tests for `multi-list` parameter type in `ParameterValidatorTest` --- tests/ParameterValidatorTest.php | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/tests/ParameterValidatorTest.php b/tests/ParameterValidatorTest.php index 59d7b2b9560..4680317a1d0 100644 --- a/tests/ParameterValidatorTest.php +++ b/tests/ParameterValidatorTest.php @@ -37,4 +37,36 @@ public function test2() ]; $this->assertNotEmpty($sut->validateInput($input, $parameters)); } + + public function test3() + { + $sut = new \ParameterValidator(); + $input = [ 'category' => [ 'economy', 'politics', 'entertainment', ], ]; + $parameters = [ + [ + 'categories' => [ + 'name' => 'Categories', + 'type' => 'multi-list', + 'values' => [ 'economy', 'politics', 'entertainment', ], + ], + ] + ]; + $this->assertSame([], $sut->validateInput($input, $parameters)); + } + + public function test4() + { + $sut = new \ParameterValidator(); + $input = [ 'categories' => [ 'economy', 'politics', 'entertainment', ], ]; + $parameters = [ + [ + 'categories' => [ + 'name' => 'Categories', + 'type' => 'multi-list', + 'values' => [ 'economy', 'politics', 'entertainment', ], + ], + ] + ]; + $this->assertNotEmpty($sut->validateInput($input, $parameters)); + } } From 564420de981b16277059dfd0e8af084ae8dcccc4 Mon Sep 17 00:00:00 2001 From: Jonatan Czarniecki Date: Sat, 7 Feb 2026 01:18:01 +0100 Subject: [PATCH 06/23] test: fix errors in tests --- tests/BridgeCardTest.php | 4 ++-- tests/ParameterValidatorTest.php | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/BridgeCardTest.php b/tests/BridgeCardTest.php index 50268f08576..e352c7c3c74 100644 --- a/tests/BridgeCardTest.php +++ b/tests/BridgeCardTest.php @@ -36,7 +36,7 @@ public function test() 'values' => [], ]; $this->assertSame('', BridgeCard::getListInput($entry, 'id', 'name')); - $this->assertSame('', BridgeCard::getListInput($entry, 'id', 'name', true)) + $this->assertSame('', BridgeCard::getListInput($entry, 'id', 'name', true)); $entry = [ 'defaultValue' => 2, @@ -45,7 +45,7 @@ public function test() ], ]; $this->assertSame('', BridgeCard::getListInput($entry, 'id', 'name')); - $this->assertSame('', BridgeCard::getListInput($entry, 'id', 'name', true)) + $this->assertSame('', BridgeCard::getListInput($entry, 'id', 'name', true)); // optgroup $entry = [ diff --git a/tests/ParameterValidatorTest.php b/tests/ParameterValidatorTest.php index 4680317a1d0..6e850f6e385 100644 --- a/tests/ParameterValidatorTest.php +++ b/tests/ParameterValidatorTest.php @@ -41,7 +41,7 @@ public function test2() public function test3() { $sut = new \ParameterValidator(); - $input = [ 'category' => [ 'economy', 'politics', 'entertainment', ], ]; + $input = [ 'categories' => [ 'economy', 'politics', 'entertainment', ], ]; $parameters = [ [ 'categories' => [ @@ -60,7 +60,7 @@ public function test4() $input = [ 'categories' => [ 'economy', 'politics', 'entertainment', ], ]; $parameters = [ [ - 'categories' => [ + 'category' => [ 'name' => 'Categories', 'type' => 'multi-list', 'values' => [ 'economy', 'politics', 'entertainment', ], From 4ac27cd16ec8211dd2beca2dbba8163e78efdedd Mon Sep 17 00:00:00 2001 From: Jonatan Czarniecki Date: Sun, 8 Feb 2026 00:14:13 +0100 Subject: [PATCH 07/23] fix: handle `defaultValue` properly for `multi-list` * make `multi-select` options selected by default when found in `defaultValue` * add needed backport of `array_find` and `array_any` --- lib/BridgeCard.php | 10 ++++++---- lib/php8backports.php | 16 ++++++++++++++++ 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/lib/BridgeCard.php b/lib/BridgeCard.php index 130c3f6d73c..fc424fc5730 100644 --- a/lib/BridgeCard.php +++ b/lib/BridgeCard.php @@ -214,8 +214,9 @@ public static function getListInput(array $entry, string $id, string $name, bool $list .= ''; foreach ($value as $subname => $subvalue) { if ( - $entry['defaultValue'] === $subname - || $entry['defaultValue'] === $subvalue + $isMulti + ? $entry['defaultValue'] && array_any($entry['defaultValue'], fn($v) => $v === $subname || $v === $subvalue) + : ($entry['defaultValue'] === $subname || $entry['defaultValue'] === $subvalue) ) { $list .= ''; } else { @@ -225,8 +226,9 @@ public static function getListInput(array $entry, string $id, string $name, bool $list .= ''; } else { if ( - $entry['defaultValue'] === $name - || $entry['defaultValue'] === $value + $isMulti + ? $entry['defaultValue'] && array_any($entry['defaultValue'], fn($v) => $v === $name || $v === $value) + : ($entry['defaultValue'] === $name || $entry['defaultValue'] === $value) ) { $list .= '' . "\n"; } else { diff --git a/lib/php8backports.php b/lib/php8backports.php index ccef6016b27..02d6709d415 100644 --- a/lib/php8backports.php +++ b/lib/php8backports.php @@ -30,3 +30,19 @@ function array_is_list(array $arr) return array_keys($arr) === range(0, count($arr) - 1); } } + +if (!function_exists('array_find')) { + function array_find(array $array, callable $callback): mixed + { + foreach ($array as $key => $value) + if ($found = $callback($value, $key)) return $value; + return null; + } +} + +if (!function_exists('array_any')) { + function array_any(array $array, callable $callback): bool + { + return !is_null(array_find($array, $callback)); + } +} From 6d2427d6c42ad3c87c6c2abbd0fc5097285028b2 Mon Sep 17 00:00:00 2001 From: Jonatan Czarniecki Date: Sun, 8 Feb 2026 00:18:20 +0100 Subject: [PATCH 08/23] fix: clarify the description in the docblock * make the dockblock description match the actual validation behavior --- middlewares/SecurityMiddleware.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/middlewares/SecurityMiddleware.php b/middlewares/SecurityMiddleware.php index e34f39220e2..d66ef7d7af8 100644 --- a/middlewares/SecurityMiddleware.php +++ b/middlewares/SecurityMiddleware.php @@ -3,7 +3,7 @@ declare(strict_types=1); /** - * Make sure that only strings and arrays are allowed in GET parameters + * Make sure that only strings and arrays of strings are allowed in GET parameters */ class SecurityMiddleware implements Middleware { From 044d8966ac91f8a544ab8c921e5900618c571efc Mon Sep 17 00:00:00 2001 From: Jonatan Czarniecki Date: Sun, 8 Feb 2026 14:41:10 +0100 Subject: [PATCH 09/23] style: add braces to comply with PSR-12 * also extract the loop logic by adding a backport of `array_all` --- lib/php8backports.php | 10 +++++++++- middlewares/SecurityMiddleware.php | 12 +++--------- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/lib/php8backports.php b/lib/php8backports.php index 02d6709d415..66efcce5845 100644 --- a/lib/php8backports.php +++ b/lib/php8backports.php @@ -34,8 +34,9 @@ function array_is_list(array $arr) if (!function_exists('array_find')) { function array_find(array $array, callable $callback): mixed { - foreach ($array as $key => $value) + foreach ($array as $key => $value) { if ($found = $callback($value, $key)) return $value; + } return null; } } @@ -46,3 +47,10 @@ function array_any(array $array, callable $callback): bool return !is_null(array_find($array, $callback)); } } + +if (!function_exists('array_all')) { + function array_all(array $array, callable $callback): bool + { + return !array_any($array, fn($v) => !$callback($v)); + } +} diff --git a/middlewares/SecurityMiddleware.php b/middlewares/SecurityMiddleware.php index d66ef7d7af8..2bfa81a345d 100644 --- a/middlewares/SecurityMiddleware.php +++ b/middlewares/SecurityMiddleware.php @@ -10,15 +10,9 @@ class SecurityMiddleware implements Middleware public function __invoke(Request $request, $next): Response { foreach ($request->toArray() as $key => $value) { - if (is_string($value)) continue; - //TODO: Maybe stricter checking for arrays? - // Not required technically because the values are properly checked - // in ParameterValidator, so basic check like this should be OK. - if (is_array($value)) { - $flag = true; - foreach ($value as $v) - if (!($flag = is_string($v))) break; - if ($flag) continue; + //TODO: verify if the type of parameter matches the type defined by the bridge in ParameterValidator + if (is_string($value) || is_array($value) && array_all($value, 'is_string')) { + continue; } return new Response(render(__DIR__ . '/../templates/error.html.php', [ 'message' => "Query parameter \"$key\" is not a string or array of strings.", From f1b33924573096b82869bca5ef021f00ce22bc89 Mon Sep 17 00:00:00 2001 From: Jonatan Czarniecki Date: Sun, 8 Feb 2026 14:46:08 +0100 Subject: [PATCH 10/23] fix(ui): make `multi-list` parameters displayed correctly * make "Find Feed from URL" display `multi-list` parameters displayed as a comma-and-space delimited string --- static/rss-bridge.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/static/rss-bridge.js b/static/rss-bridge.js index 9cd004cb00c..8daaa6a10a6 100644 --- a/static/rss-bridge.js +++ b/static/rss-bridge.js @@ -50,7 +50,7 @@ var rssbridge_feed_finder = (function() { */ // Start the Feed search - async function rssbridge_feed_search(event) { + async function rssbridge_feed_search(_) { const input = document.getElementById('searchfield'); let content = encodeURIComponent(input.value); if (content) { @@ -92,7 +92,9 @@ var rssbridge_feed_finder = (function() { // Now display every Feed parameter for (const param in element.bridgeData) { - content += `
  • ${element.bridgeData[param].name} : ${element.bridgeData[param].value}
  • `; + const paramMeta = element.bridgeMeta.parameters[element.bridgeParams.context][param] ?? element.bridgeMeta.parameters['global'][param]; + const value = paramMeta?.type === 'multi-list' ? element.bridgeData[param].value.join(', ') : element.bridgeData[param].value + content += `
  • ${element.bridgeData[param].name} : ${value}
  • `; } content += ` From ad1738b0fafb7ee75275a2034ad81ece24ea8d41 Mon Sep 17 00:00:00 2001 From: Jonatan Czarniecki Date: Sun, 8 Feb 2026 15:03:28 +0100 Subject: [PATCH 11/23] fix: make backported array functions behave correctly * add `array_find_key` to rely on the fact that, unlike values, keys cannot be `null` --- lib/php8backports.php | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/lib/php8backports.php b/lib/php8backports.php index 66efcce5845..116ef8511cd 100644 --- a/lib/php8backports.php +++ b/lib/php8backports.php @@ -31,26 +31,35 @@ function array_is_list(array $arr) } } -if (!function_exists('array_find')) { - function array_find(array $array, callable $callback): mixed +if (!function_exists('array_find_key')) { + function array_find_key(array $array, callable $callback) { foreach ($array as $key => $value) { - if ($found = $callback($value, $key)) return $value; + if ($callback($value, $key)) { + return $key; + } } return null; } } +if (!function_exists('array_find')) { + function array_find(array $array, callable $callback) + { + return (($key = array_find_key($array, $callback)) !== null) ? $array[$key] : null; + } +} + if (!function_exists('array_any')) { function array_any(array $array, callable $callback): bool { - return !is_null(array_find($array, $callback)); + return array_find_key($array, $callback) !== null; } } if (!function_exists('array_all')) { function array_all(array $array, callable $callback): bool { - return !array_any($array, fn($v) => !$callback($v)); + return !array_any($array, fn($v, $k) => !$callback($v, $k)); } } From d84f7f2aaefbb7dda88e487e3896e05e7024b0bf Mon Sep 17 00:00:00 2001 From: Jonatan Czarniecki Date: Sun, 8 Feb 2026 15:58:07 +0100 Subject: [PATCH 12/23] fix: validate the parameter type correctly * add type check to every method in `ParameterValidator` as they assumed all passed to be of type `string` - now the value can also be an array (of strings) * fix `multi-list` validation logic and move it to a separate method --- lib/ParameterValidator.php | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/lib/ParameterValidator.php b/lib/ParameterValidator.php index 488472c8487..d0977201c25 100644 --- a/lib/ParameterValidator.php +++ b/lib/ParameterValidator.php @@ -32,8 +32,7 @@ public function validateInput(array &$input, $contexts): array $input[$name] = $this->validateListValue($value, $contextParameters[$name]['values']); break; case 'multi-list': - //TODO: Maybe move the array checking and looping through all of the values to another method? - $input[$name] = is_array($value) ? array_map(fn($v) => $this->validateListValue($v, $contextParameters[$name]['values']), $value) : null; + $input[$name] = $this->validateMultiListValue($value, $contextParameters[$name]['values']); break; default: case 'text': @@ -133,6 +132,9 @@ public function getQueriedContext(array $input, array $contexts) private function validateTextValue($value, $pattern = null) { + if (!is_scalar($value)) { + return null; + } if (is_null($pattern)) { // No filtering taking place $filteredValue = filter_var($value); @@ -147,6 +149,9 @@ private function validateTextValue($value, $pattern = null) private function validateNumberValue($value) { + if (!is_scalar($value)) { + return null; + } $filteredValue = filter_var($value, FILTER_VALIDATE_INT); if ($filteredValue === false) { return null; @@ -156,11 +161,17 @@ private function validateNumberValue($value) private function validateCheckboxValue($value) { + if (!is_scalar($value)) { + return null; + } return filter_var($value, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE); } private function validateListValue($value, $expectedValues) { + if (!is_scalar($value)) { + return null; + } $filteredValue = filter_var($value); if ($filteredValue === false) { return null; @@ -176,4 +187,19 @@ private function validateListValue($value, $expectedValues) } return $filteredValue; } + + private function validateMultiListValue($values, $expectedValues) + { + if (!is_array($values)) { + return null; + } + $filteredValues = []; + foreach ($values as $v) { + $filteredValue = $this->validateListValue($v, $expectedValues) + if ($filtered === null) { + return null; + } + $filteredValues[] = $filtered; + } + } } From 9b7cddfcfbf51b782391073f66605cf8b75e4aa9 Mon Sep 17 00:00:00 2001 From: Jonatan Czarniecki Date: Sun, 8 Feb 2026 17:12:19 +0100 Subject: [PATCH 13/23] fixup! fix: validate the parameter type correctly --- lib/ParameterValidator.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/ParameterValidator.php b/lib/ParameterValidator.php index d0977201c25..19270cc22a2 100644 --- a/lib/ParameterValidator.php +++ b/lib/ParameterValidator.php @@ -195,11 +195,12 @@ private function validateMultiListValue($values, $expectedValues) } $filteredValues = []; foreach ($values as $v) { - $filteredValue = $this->validateListValue($v, $expectedValues) - if ($filtered === null) { + $filteredValue = $this->validateListValue($v, $expectedValues); + if ($filteredValue === null) { return null; } - $filteredValues[] = $filtered; + $filteredValues[] = $filteredValue; } + return $filteredValues; } } From 79ceb942b71a1ef0904df2f488a4921ac567ccc1 Mon Sep 17 00:00:00 2001 From: Jonatan Czarniecki Date: Sun, 8 Feb 2026 17:12:41 +0100 Subject: [PATCH 14/23] fixup! fix(ui): make `multi-list` parameters displayed correctly --- static/rss-bridge.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/static/rss-bridge.js b/static/rss-bridge.js index 8daaa6a10a6..2ddf9fffcd7 100644 --- a/static/rss-bridge.js +++ b/static/rss-bridge.js @@ -92,8 +92,8 @@ var rssbridge_feed_finder = (function() { // Now display every Feed parameter for (const param in element.bridgeData) { - const paramMeta = element.bridgeMeta.parameters[element.bridgeParams.context][param] ?? element.bridgeMeta.parameters['global'][param]; - const value = paramMeta?.type === 'multi-list' ? element.bridgeData[param].value.join(', ') : element.bridgeData[param].value + const paramMeta = element.bridgeMeta.parameters[element.bridgeParams.context]?.[param] ?? element.bridgeMeta.parameters['global']?.[param]; + const value = paramMeta?.type === 'multi-list' ? element.bridgeData[param].value.join(', ') : element.bridgeData[param].value; content += `
  • ${element.bridgeData[param].name} : ${value}
  • `; } content += ` From 1f871e2bee2f35f96c5816754e26bfb6f3d17a46 Mon Sep 17 00:00:00 2001 From: Jonatan Czarniecki Date: Sun, 8 Feb 2026 17:55:24 +0100 Subject: [PATCH 15/23] fixup! style: add braces to comply with PSR-12 --- middlewares/SecurityMiddleware.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/middlewares/SecurityMiddleware.php b/middlewares/SecurityMiddleware.php index 2bfa81a345d..b16dba98f8e 100644 --- a/middlewares/SecurityMiddleware.php +++ b/middlewares/SecurityMiddleware.php @@ -10,8 +10,7 @@ class SecurityMiddleware implements Middleware public function __invoke(Request $request, $next): Response { foreach ($request->toArray() as $key => $value) { - //TODO: verify if the type of parameter matches the type defined by the bridge in ParameterValidator - if (is_string($value) || is_array($value) && array_all($value, 'is_string')) { + if (is_string($value) || is_array($value) && array_all($value, fn($v, $k) => is_string($v))) { continue; } return new Response(render(__DIR__ . '/../templates/error.html.php', [ From 4843b0b92b67dd84e7d390fd2c444bd0fcfd6f7b Mon Sep 17 00:00:00 2001 From: Jonatan Czarniecki Date: Tue, 10 Feb 2026 00:55:40 +0100 Subject: [PATCH 16/23] style: fix line length --- tests/BridgeCardTest.php | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/tests/BridgeCardTest.php b/tests/BridgeCardTest.php index 5311a07af83..89c56b2b9ee 100644 --- a/tests/BridgeCardTest.php +++ b/tests/BridgeCardTest.php @@ -23,8 +23,14 @@ public function test() 'foo' => 'bar', ], ]; - $this->assertSame('' . "\n", BridgeCard::getListInput($entry, 'id', 'name')); - $this->assertSame('' . "\n", BridgeCard::getListInput($entry, 'id', 'name', true)); + $this->assertSame( + '' . "\n", + BridgeCard::getListInput($entry, 'id', 'name') + ); + $this->assertSame( + '' . "\n", + BridgeCard::getListInput($entry, 'id', 'name', true) + ); // optgroup $entry = [ From 735b5d55c2286994874110b13b578f3088394420 Mon Sep 17 00:00:00 2001 From: Jonatan Czarniecki Date: Tue, 10 Feb 2026 00:59:00 +0100 Subject: [PATCH 17/23] feat: support for scalar `defaultValue` for `multi-list` --- lib/BridgeAbstract.php | 5 +++++ lib/BridgeCard.php | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/BridgeAbstract.php b/lib/BridgeAbstract.php index f08d201f888..278ef96e2f2 100644 --- a/lib/BridgeAbstract.php +++ b/lib/BridgeAbstract.php @@ -224,6 +224,11 @@ private function setInputWithContext(array $input, $queriedContext) } else { $this->inputs[$context][$name]['value'] = $properties['defaultValue']; } + case 'multi-list': + if (isset($properties['defaultValue'])) { + // Casting to array makes scalar values, like 'my value', become arrays, like ['my value']. + $this->inputs[$context][$name]['value'] = (array)($properties['defaultValue'] ?? []); + } break; default: if (isset($properties['defaultValue'])) { diff --git a/lib/BridgeCard.php b/lib/BridgeCard.php index 667fe1173a1..a676dcc4168 100644 --- a/lib/BridgeCard.php +++ b/lib/BridgeCard.php @@ -141,7 +141,7 @@ private static function renderForm( } if (!isset($inputEntry['defaultValue'])) { - $inputEntry['defaultValue'] = ''; + $inputEntry['defaultValue'] = $inputEntry['type'] === 'multi-list' ? [] : ''; } $idArg = 'arg-' . urlencode($bridgeClassName) . '-' . urlencode($contextName) . '-' . urlencode($id); From 4c41ad57405d7fb39ea74e40c55a611b3d435f0b Mon Sep 17 00:00:00 2001 From: Jonatan Czarniecki Date: Tue, 10 Feb 2026 01:01:40 +0100 Subject: [PATCH 18/23] fixup! feat: support for scalar `defaultValue` for `multi-list` --- lib/BridgeAbstract.php | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/BridgeAbstract.php b/lib/BridgeAbstract.php index 278ef96e2f2..8013e6880b8 100644 --- a/lib/BridgeAbstract.php +++ b/lib/BridgeAbstract.php @@ -224,6 +224,7 @@ private function setInputWithContext(array $input, $queriedContext) } else { $this->inputs[$context][$name]['value'] = $properties['defaultValue']; } + break; case 'multi-list': if (isset($properties['defaultValue'])) { // Casting to array makes scalar values, like 'my value', become arrays, like ['my value']. From b2ebe01a2f7f1cf7b1028508bd1ef7d9a427c352 Mon Sep 17 00:00:00 2001 From: Jonatan Czarniecki Date: Tue, 10 Feb 2026 01:57:41 +0100 Subject: [PATCH 19/23] fix: detect empty values inside `getListInput` * don't try to access a possibly undefined array key --- lib/BridgeCard.php | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/lib/BridgeCard.php b/lib/BridgeCard.php index a676dcc4168..e9ff8bd204d 100644 --- a/lib/BridgeCard.php +++ b/lib/BridgeCard.php @@ -140,10 +140,6 @@ private static function renderForm( $inputEntry['exampleValue'] = ''; } - if (!isset($inputEntry['defaultValue'])) { - $inputEntry['defaultValue'] = $inputEntry['type'] === 'multi-list' ? [] : ''; - } - $idArg = 'arg-' . urlencode($bridgeClassName) . '-' . urlencode($contextName) . '-' . urlencode($id); $form .= html_tag('label', $inputEntry['name'], ['for' => $idArg]) . "\n"; @@ -205,7 +201,7 @@ private static function renderForm( public static function getTextInput(array $entry, string $id, string $name): string { $pattern = $entry['pattern'] ?? null; - $checked = $entry['defaultValue'] === 'checked'; + $checked = ($entry['defaultValue'] ??= '') === 'checked'; $required = $entry['required'] ?? false; return html_input([ @@ -223,7 +219,7 @@ public static function getTextInput(array $entry, string $id, string $name): str public static function getNumberInput(array $entry, string $id, string $name): string { $pattern = $entry['pattern'] ?? null; - $checked = $entry['defaultValue'] === 'checked'; + $checked = ($entry['defaultValue'] ??= '') === 'checked'; $required = $entry['required'] ?? false; return html_input([ @@ -244,17 +240,20 @@ public static function getCheckboxInput(array $entry, string $id, string $name): 'id' => $id, 'type' => 'checkbox', 'name' => $name, - 'checked' => $entry['defaultValue'] === 'checked', + 'checked' => ($entry['defaultValue'] ??= '') === 'checked', ]); } public static function getListInput(array $entry, string $id, string $name, bool $isMulti = false): string { - $list = sprintf('', $id, $name . ($isMulti ? '[]' : ''), $isMulti ? ' multiple' : '') . "\n"; foreach ($entry['values'] as $name => $value) { if (is_array($value)) { From ea6ffcdd9f1b8ed886c2452292d9a566b6503e9a Mon Sep 17 00:00:00 2001 From: Jonatan Czarniecki Date: Tue, 10 Feb 2026 22:19:35 +0100 Subject: [PATCH 20/23] style: fix line exceeding character limit --- actions/FrontpageAction.php | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/actions/FrontpageAction.php b/actions/FrontpageAction.php index 5892f47ab3e..4d96aedfd69 100644 --- a/actions/FrontpageAction.php +++ b/actions/FrontpageAction.php @@ -290,11 +290,11 @@ public static function getCheckboxInput(array $parameter, string $id, string $na public static function getListInput(array $parameter, string $id, string $name, bool $isMulti = false): string { - $parameter['defaultValue'] ??= $isMulti ? [] : null; + $default = $parameter['defaultValue'] ?? ($isMulti ? [] : null); // Cast to array, so scalars become single element arrays - `"default value"` becomes `["default value"]`. // Flip, so the values become keys and we can access the values later with O(1) complexity. if ($isMulti) { - $flip = array_flip((array)($parameter['defaultValue'])); + $default = array_flip((array)($default)); } $list = sprintf('', $id, $name . ($isMulti ? '[]' : ''), $isMulti ? ' multiple' : '') . "\n"; - foreach ($parameter['values'] as $name => $value) { - if (is_array($value)) { - $list .= ''; - foreach ($value as $subname => $subvalue) { + if (!empty($parameter['values'])) { + $default = $parameter['defaultValue']; + // Cast to array, so scalars become single element arrays - `"default value"` becomes `["default value"]`. + // Flip, so the values become keys and we can access the values later with O(1) complexity. + if ($isMulti) { + $default = array_flip((array)($default)); + } + + foreach ($parameter['values'] as $name => $value) { + if (is_array($value)) { + $list .= ''; + foreach ($value as $subname => $subvalue) { + if ($isMulti) { + $selected = isset($default[$subname]) || isset($default[$subvalue]); + } else { + $selected = $default === $subname || $default === $subvalue; + } + $list .= html_option($subname, $subvalue, $selected) . "\n"; + } + $list .= ''; + } else { if ($isMulti) { - $selected = isset($default[$subname]) || isset($default[$subvalue]); + $selected = isset($default[$name]) || isset($default[$value]); } else { - $selected = $default === $subname || $default === $subvalue; + $selected = $default === $name || $default === $value; } - $list .= html_option($subname, $subvalue, $selected) . "\n"; - } - $list .= ''; - } else { - if ($isMulti) { - $selected = isset($default[$name]) || isset($default[$value]); - } else { - $selected = $default === $name || $default === $value; + $list .= html_option($name, $value, $selected) . "\n"; } - $list .= html_option($name, $value, $selected) . "\n"; } } From f57ce1962df7d1ec4be1f926c2da4934a4593acd Mon Sep 17 00:00:00 2001 From: Jonatan Czarniecki Date: Thu, 12 Feb 2026 00:52:11 +0100 Subject: [PATCH 23/23] test: add test for multiple default values --- tests/BridgeCardTest.php | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/tests/BridgeCardTest.php b/tests/BridgeCardTest.php index f652e9f6501..b2ac3e8d69f 100644 --- a/tests/BridgeCardTest.php +++ b/tests/BridgeCardTest.php @@ -48,7 +48,22 @@ public function test() FrontpageAction::getListInput($entry, 'id', 'name', true) ); - //TODO: add test for mutli-list with array defaultValue + // multiple default values + $entry = [ + 'defaultValue' => [ 'a', 'c', ], + 'values' => ['kek' => [ + 'f' => 'b', + 'a' => 'g', + ]], + ]; + $expected = << + + + + +EOT; + $this->assertSame($expected, FrontpageAction::getListInput($entry, 'id', 'name', true)); } public function test2()