diff --git a/CHANGELOG.md b/CHANGELOG.md index 6f130519..9b9deca8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). +## [Unreleased] + +### Fixed + +- Fix mandatory fields of a block hidden by its display conditions blocking the save of an item + ## [1.25.0] - 2026-10-06 ### Added diff --git a/inc/container.class.php b/inc/container.class.php index 2c023504..e94d1752 100644 --- a/inc/container.class.php +++ b/inc/container.class.php @@ -2215,6 +2215,12 @@ private static function checkContainerMandatory(CommonDBTM $item, PluginFieldsCo return []; } + //container hidden by its display conditions: its fields can't be filled + $display_condition = new PluginFieldsContainerDisplayCondition(); + if (!$display_condition->computeDisplayContainer($item, $c_id)) { + return []; + } + $status_field_name = PluginFieldsStatusOverride::getStatusFieldName($item::class); $data = ['plugin_fields_containers_id' => $c_id]; if ($item->input['_auto_import'] ?? false) { diff --git a/inc/field.class.php b/inc/field.class.php index fcca8817..9144f8cb 100644 --- a/inc/field.class.php +++ b/inc/field.class.php @@ -1349,7 +1349,7 @@ public static function prepareHtmlFields( $value = is_array($decoded) ? $decoded : []; } - if ($field['multiple'] && is_array($value)) { + if ($field['multiple']) { // Flatten any nested arrays caused by corrupted DB data (double-encoded values) // so that Dropdown::show() always receives a flat list of scalars. $value = array_values(array_filter( diff --git a/tests/Units/ContainerItemUpdateTest.php b/tests/Units/ContainerItemUpdateTest.php index 3809d60e..9152d69b 100644 --- a/tests/Units/ContainerItemUpdateTest.php +++ b/tests/Units/ContainerItemUpdateTest.php @@ -35,6 +35,7 @@ use Glpi\Tests\GLPITestCase; use GlpiPlugin\Field\Tests\FieldTestTrait; use PluginFieldsContainer; +use PluginFieldsContainerDisplayCondition; use Ticket; use Entity; use Notification; @@ -1037,4 +1038,76 @@ public function testSuccessiveUpdatesOverwritePluginFieldValues(): void 'Plugin value must not be erased by a native-only update.', ); } + + /** + * A mandatory DOM field must not block creation/update when its container is + * hidden by a display condition. + */ + public function testNotBlockedWhenMandatoryDomContainerIsHidden(): void + { + $this->login(); + + $container = $this->createFieldContainer([ + 'label' => 'Hidden Mandatory Dom Container', + 'type' => 'dom', + 'itemtypes' => [Ticket::class], + 'is_active' => 1, + 'entities_id' => 0, + 'is_recursive' => 1, + ]); + $this->createField([ + 'label' => 'Mandatory Field', + 'type' => 'text', + PluginFieldsContainer::getForeignKeyField() => $container->getID(), + 'ranking' => 1, + 'is_active' => 1, + 'is_readonly' => 0, + 'mandatory' => 1, + ]); + + // Hide the container for incidents. + $this->createItem(PluginFieldsContainerDisplayCondition::class, [ + PluginFieldsContainer::getForeignKeyField() => $container->getID(), + 'itemtype' => Ticket::class, + 'search_option' => 14, // type + 'condition' => PluginFieldsContainerDisplayCondition::SHOW_CONDITION_EQ, + 'value' => Ticket::INCIDENT_TYPE, + ]); + + // The container is visible for requests + $ticket = new Ticket(); + $ticket_id = $ticket->add([ + 'name' => 'Request with visible mandatory field', + 'content' => 'Test', + 'entities_id' => 0, + 'type' => Ticket::DEMAND_TYPE, + ]); + $this->assertFalse($ticket_id, 'A mandatory field in a visible container must block creation.'); + $this->hasSessionMessageThatContains( + __('Some mandatory fields are empty', 'fields'), + ERROR, + ); + $this->hasSessionMessageThatContains( + __('The form or source creating this item does not provide the mandatory fields above: map them to it, or make them optional.', 'fields'), + ERROR, + ); + + $ticket = new Ticket(); + $ticket = $this->createItem(Ticket::class, [ + 'name' => 'Incident with hidden mandatory field', + 'content' => 'Test', + 'entities_id' => 0, + 'type' => Ticket::INCIDENT_TYPE, + ]); + + $is_updated = $ticket->update([ + 'id' => $ticket->getID(), + 'type' => Ticket::DEMAND_TYPE, + ]); + $this->assertFalse($is_updated); + $this->hasSessionMessageThatContains( + __('Some mandatory fields are empty', 'fields'), + ERROR, + ); + } } diff --git a/tests/Units/MassiveActionGlpiItemDropdownTest.php b/tests/Units/MassiveActionGlpiItemDropdownTest.php index 12663d61..e77c164e 100644 --- a/tests/Units/MassiveActionGlpiItemDropdownTest.php +++ b/tests/Units/MassiveActionGlpiItemDropdownTest.php @@ -64,7 +64,7 @@ public function setUp(): void public function tearDown(): void { - unset($_REQUEST['massiveaction'], $_POST); + unset($_REQUEST['massiveaction']); $this->tearDownFieldTest(); GLPITestCase::tearDown(); } diff --git a/tests/Units/MassiveActionRealFlowTest.php b/tests/Units/MassiveActionRealFlowTest.php index 488809a2..1d373160 100644 --- a/tests/Units/MassiveActionRealFlowTest.php +++ b/tests/Units/MassiveActionRealFlowTest.php @@ -62,7 +62,7 @@ public function setUp(): void public function tearDown(): void { - unset($_REQUEST['massiveaction'], $_POST); + unset($_REQUEST['massiveaction']); $this->tearDownFieldTest(); GLPITestCase::tearDown(); }