From 197b602b437ec1ac48a64c3863566e0354cf00b8 Mon Sep 17 00:00:00 2001 From: alisher372 Date: Sun, 2 Aug 2026 11:52:39 +0500 Subject: [PATCH 1/2] Fix Custom Dropdown filtering in table questions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When using GLPI Custom Dropdowns inside table questions, all dropdown definitions display the same combined list of values. This happens because all custom dropdowns use the same database table (`glpi_dropdowns_dropdowns`), but the current implementation does not apply the system criteria that distinguish each dropdown definition. ## Solution This change updates the table question implementation to correctly filter Custom Dropdown values by applying the item's `getSystemSQLCriteria()` when: - loading dropdown options; - resolving stored values. As a result, each Custom Dropdown now displays only the values that belong to its own definition. Before Снимок экрана 2026-07-21 143100 Снимок экрана 2026-07-21 143054 After Screenshot 2026-07-31 163806 Screenshot 2026-07-31 163809 --- src/Model/QuestionType/TableQuestion.php | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/src/Model/QuestionType/TableQuestion.php b/src/Model/QuestionType/TableQuestion.php index 84889cf..84dc63f 100644 --- a/src/Model/QuestionType/TableQuestion.php +++ b/src/Model/QuestionType/TableQuestion.php @@ -553,11 +553,22 @@ private function resolveItemNames(string $itemtype, array $values): array global $DB; + $where = ['id' => $ids]; + + // GLPI 11 custom dropdown classes can share the same database table. + // Their system SQL criteria restrict rows to the current dropdown definition. + if (method_exists($itemtype, 'getSystemSQLCriteria')) { + $system_criteria = $itemtype::getSystemSQLCriteria(); + if (is_array($system_criteria) && $system_criteria !== []) { + $where[] = $system_criteria; + } + } + $map = []; foreach ($DB->request([ 'SELECT' => ['id', 'name'], 'FROM' => $item->getTable(), - 'WHERE' => ['id' => $ids], + 'WHERE' => $where, ]) as $row) { if (!is_array($row)) { continue; @@ -950,6 +961,15 @@ private function buildGlpiItemtypeOptions(string $itemtype): array $where = []; + // GLPI 11 custom dropdown classes can share the same database table. + // Their system SQL criteria restrict rows to the current dropdown definition. + if (method_exists($itemtype, 'getSystemSQLCriteria')) { + $system_criteria = $itemtype::getSystemSQLCriteria(); + if (is_array($system_criteria) && $system_criteria !== []) { + $where[] = $system_criteria; + } + } + if ($item->maybeDeleted()) { $where['is_deleted'] = 0; } @@ -998,4 +1018,4 @@ private function loadConfig(Question $question): TableQuestionConfig $config = $this->getExtraDataConfig($decoded); return $config instanceof TableQuestionConfig ? $config : new TableQuestionConfig(); } -} +} \ No newline at end of file From 974a5177f34fffd9023cecee8f743ec0498291f5 Mon Sep 17 00:00:00 2001 From: alisher372 Date: Fri, 7 Aug 2026 16:04:38 +0500 Subject: [PATCH 2/2] Add regression tests for custom dropdown filtering - Add regression tests for Custom Dropdown filtering - Update CHANGELOG - Fix PHP-CS-Fixer formatting --- CHANGELOG.md | 4 + src/Model/QuestionType/TableQuestion.php | 2 +- .../Model/QuestionType/TableQuestionTest.php | 88 +++++++++++++++++++ 3 files changed, 93 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5539b04..87b28b9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +### Fixed + +- Fixed Custom Dropdown filtering in table questions to only display values from the configured dropdown definition + ## [1.2.0] - 2026-07-28 ### Add diff --git a/src/Model/QuestionType/TableQuestion.php b/src/Model/QuestionType/TableQuestion.php index 84dc63f..5fc3fd0 100644 --- a/src/Model/QuestionType/TableQuestion.php +++ b/src/Model/QuestionType/TableQuestion.php @@ -1018,4 +1018,4 @@ private function loadConfig(Question $question): TableQuestionConfig $config = $this->getExtraDataConfig($decoded); return $config instanceof TableQuestionConfig ? $config : new TableQuestionConfig(); } -} \ No newline at end of file +} diff --git a/tests/Model/QuestionType/TableQuestionTest.php b/tests/Model/QuestionType/TableQuestionTest.php index 24c8e99..9108744 100644 --- a/tests/Model/QuestionType/TableQuestionTest.php +++ b/tests/Model/QuestionType/TableQuestionTest.php @@ -33,6 +33,8 @@ namespace GlpiPlugin\Advancedforms\Tests\Model\QuestionType; +use Glpi\Dropdown\DropdownDefinition; +use Glpi\Dropdown\DropdownDefinitionManager; use Glpi\Form\QuestionType\QuestionTypeCheckbox; use Glpi\Form\QuestionType\QuestionTypeEmail; use Glpi\Form\QuestionType\QuestionTypeFile; @@ -45,6 +47,7 @@ use GlpiPlugin\Advancedforms\Model\QuestionType\TableQuestion; use GlpiPlugin\Advancedforms\Model\QuestionType\TableQuestionConfig; use GlpiPlugin\Advancedforms\Tests\AdvancedFormsTestCase; +use ReflectionMethod; final class TableQuestionTest extends AdvancedFormsTestCase { @@ -247,4 +250,89 @@ public function testTransformConditionValueSkipsNonArrayRows(): void $result = $this->type->transformConditionValueForComparisons($answer, null); $this->assertSame(['10.0.0.1'], $result); } + + public function testCustomDropdownOptionsAreRestrictedToConfiguredDefinition(): void + { + [$itemtype, $allowed_id, $other_id] = $this->createCustomDropdownFixture( + 'af_table_options', + ); + + $method = new ReflectionMethod( + TableQuestion::class, + 'buildGlpiItemtypeOptions', + ); + + $options = $method->invoke($this->type, $itemtype); + + $this->assertIsArray($options); + $this->assertArrayHasKey((string) $allowed_id, $options); + $this->assertSame('Allowed option', $options[(string) $allowed_id]); + $this->assertArrayNotHasKey((string) $other_id, $options); + } + + public function testCustomDropdownStoredValuesAreRestrictedToConfiguredDefinition(): void + { + [$itemtype, $allowed_id, $other_id] = $this->createCustomDropdownFixture( + 'af_table_values', + ); + + $method = new ReflectionMethod( + TableQuestion::class, + 'resolveItemNames', + ); + + $values = $method->invoke( + $this->type, + $itemtype, + [(string) $allowed_id, (string) $other_id], + ); + + $this->assertIsArray($values); + $this->assertArrayHasKey((string) $allowed_id, $values); + $this->assertSame('Allowed option', $values[(string) $allowed_id]); + $this->assertArrayNotHasKey((string) $other_id, $values); + } + + /** + * @return array{class-string, int, int} + */ + private function createCustomDropdownFixture(string $system_name): array + { + $allowed_definition = $this->createItem(DropdownDefinition::class, [ + 'system_name' => $system_name . '_allowed', + 'label' => 'Allowed dropdown', + 'is_active' => 1, + ]); + + $other_definition = $this->createItem(DropdownDefinition::class, [ + 'system_name' => $system_name . '_other', + 'label' => 'Other dropdown', + 'is_active' => 1, + ]); + + // Refresh definitions so GLPI can autoload both concrete + // Glpi\CustomDropdown classes created above. + DropdownDefinitionManager::getInstance()->bootDefinitions(); + + $allowed_itemtype = $allowed_definition->getDropdownClassName(); + $other_itemtype = $other_definition->getDropdownClassName(); + + $this->assertTrue(class_exists($allowed_itemtype)); + $this->assertTrue(class_exists($other_itemtype)); + + $allowed_item = $this->createItem($allowed_itemtype, [ + 'name' => 'Allowed option', + ]); + + $other_item = $this->createItem($other_itemtype, [ + 'name' => 'Other option', + ]); + + return [ + $allowed_itemtype, + (int) $allowed_item->getID(), + (int) $other_item->getID(), + ]; + } + }