From b71f979f28eef769a8700b4ce81761389ee0cf87 Mon Sep 17 00:00:00 2001 From: MyuTsu Date: Tue, 1 Sep 2026 11:38:44 +0200 Subject: [PATCH 1/2] fix(container): save default field value when empty on creation --- inc/container.class.php | 16 ++++ inc/field.class.php | 40 ++++++---- tests/FieldTestCase.php | 4 +- tests/Units/ContainerTest.php | 142 ++++++++++++++++++++++++++++++++++ 4 files changed, 187 insertions(+), 15 deletions(-) diff --git a/inc/container.class.php b/inc/container.class.php index 0c0b2ebb..06c267f0 100644 --- a/inc/container.class.php +++ b/inc/container.class.php @@ -2092,6 +2092,13 @@ private static function populateData($c_id, CommonDBTM $item) $data[$multiple_key] = $_POST[$multiple_key]; $has_fields = true; } + } elseif ($item->isNewItem()) { + $default = PluginFieldsField::getDefaultValue($field); + $decoded = json_decode((string) $default, true); + if (is_array($decoded) && $decoded !== []) { + $data[$multiple_key] = $decoded; + $has_fields = true; + } } } @@ -2105,6 +2112,15 @@ private static function populateData($c_id, CommonDBTM $item) $data[$field['name']] = []; } } + } elseif ($item->isNewItem()) { + $default = PluginFieldsField::getDefaultValue($field); + if ($default !== null) { + $default_key = $field['type'] === 'dropdown' + ? 'plugin_fields_' . $field['name'] . 'dropdowns_id' + : $field['name']; + $data[$default_key] = $default; + $has_fields = true; + } } } diff --git a/inc/field.class.php b/inc/field.class.php index 07f25d69..39980c35 100644 --- a/inc/field.class.php +++ b/inc/field.class.php @@ -1144,6 +1144,32 @@ function(evt) { ); } + /** + * Retrieves the default value of a field + * + * @return mixed + */ + public static function getDefaultValue(array $field) + { + $value = null; + + if (in_array($field['type'], ['dropdown', 'yesno']) && $field['default_value'] === '') { + $value = 0; + } elseif ($field['default_value'] !== '') { + $value = $field['default_value']; + + // shortcut for date/datetime + if ( + in_array($field['type'], ['date', 'datetime']) + && $value == 'now' + ) { + $value = $_SESSION['glpi_currenttime']; + } + } + + return $value; + } + public static function prepareHtmlFields( $fields, $item, @@ -1295,19 +1321,7 @@ public static function prepareHtmlFields( //get default value if ($value === null) { - if (in_array($field['type'], ['dropdown', 'yesno']) && $field['default_value'] === '') { - $value = 0; - } elseif ($field['default_value'] !== '') { - $value = $field['default_value']; - - // shortcut for date/datetime - if ( - in_array($field['type'], ['date', 'datetime']) - && $value == 'now' - ) { - $value = $_SESSION['glpi_currenttime']; - } - } + $value = self::getDefaultValue($field); } if ($field['multiple'] && !is_array($value)) { diff --git a/tests/FieldTestCase.php b/tests/FieldTestCase.php index 0cb8e04d..143f2c55 100644 --- a/tests/FieldTestCase.php +++ b/tests/FieldTestCase.php @@ -93,12 +93,12 @@ public function createFieldContainer(array $inputs): PluginFieldsContainer return $container; } - public function createField(array $inputs): PluginFieldsField + public function createField(array $inputs, array $skip_fields = []): PluginFieldsField { // Re-login to ensure we are logged in $this->login(); - $field = $this->createItem(PluginFieldsField::class, $inputs, ['allowed_values', 'question_types']); + $field = $this->createItem(PluginFieldsField::class, $inputs, array_merge(['allowed_values', 'question_types'], $skip_fields)); self::$createdFields[] = $field; // Re-initialize fields plugin to register new field logic diff --git a/tests/Units/ContainerTest.php b/tests/Units/ContainerTest.php index 9d7c2cd1..fec1b06e 100644 --- a/tests/Units/ContainerTest.php +++ b/tests/Units/ContainerTest.php @@ -36,9 +36,15 @@ use Glpi\Tests\DbTestCase; use Glpi\Tests\GLPITestCase; use GlpiPlugin\Field\Tests\FieldTestTrait; +use Laminas\Mail\Storage\Message; +use MailCollector; use PHPUnit\Framework\Attributes\DataProvider; use PluginFieldsContainer; +use PluginFieldsDropdown; +use PluginFieldsField; +use Session; use Ticket; +use UserEmail; require_once __DIR__ . '/../FieldTestCase.php'; @@ -124,4 +130,140 @@ public function testAddDomtabWithIncompatibleItemtypeIsRejected(): void ]); $this->assertFalse($result); } + + public static function provideMandatoryFieldTypes(): iterable + { + yield 'text' => ['type' => 'text', 'default_value' => 'Text default', 'expected_value' => 'Text default']; + yield 'textarea' => ['type' => 'textarea', 'default_value' => 'Textarea default', 'expected_value' => 'Textarea default']; + yield 'url' => ['type' => 'url', 'default_value' => 'https://example.org', 'expected_value' => 'https://example.org']; + yield 'number' => ['type' => 'number', 'default_value' => '42', 'expected_value' => '42']; + yield 'date' => ['type' => 'date', 'default_value' => '2024-01-01', 'expected_value' => '2024-01-01']; + yield 'datetime' => ['type' => 'datetime', 'default_value' => '2024-01-01 10:00:00', 'expected_value' => '2024-01-01 10:00:00']; + yield 'dropdown' => ['type' => 'dropdown', 'default_value' => null, 'expected_value' => null, 'multiple' => false]; + yield 'dropdown multiple' => ['type' => 'dropdown', 'default_value' => null, 'expected_value' => null, 'multiple' => true]; + } + + #[DataProvider('provideMandatoryFieldTypes')] + public function testMailCollectorImportRespectsMandatoryFieldDefaultValue( + string $type, + array|string|null $default_value, + array|int|string|null $expected_value, + bool $multiple = false, + ): void { + $this->login(); + + $container = $this->createFieldContainer([ + 'label' => 'Mail Collector ' . $type . ($multiple ? ' Multi' : '') . ' Container', + 'type' => 'dom', + 'itemtypes' => [Ticket::class], + 'is_active' => 1, + 'entities_id' => 0, + 'is_recursive' => 1, + ]); + + $field_input = [ + 'label' => 'Mandatory ' . $type . ($multiple ? ' multiple' : ''), + 'type' => $type, + 'multiple' => $multiple ? 1 : 0, + PluginFieldsContainer::getForeignKeyField() => $container->getID(), + 'ranking' => 1, + 'is_active' => 1, + 'is_readonly' => 0, + 'mandatory' => 1, + ]; + + if ($multiple) { + $field_input['default_value'] = []; + } + + $field = $this->createField($field_input, $multiple ? ['default_value'] : []); + + $field_name = $field->fields['name']; + $row_key = $type === 'dropdown' ? 'plugin_fields_' . $field_name . 'dropdowns_id' : $field_name; + + if ($type === 'dropdown') { + $dropdown_classname = PluginFieldsDropdown::getClassname($field_name); + + if ($multiple) { + $option_ids = [ + $this->createItem($dropdown_classname, ['name' => 'Default option 1'])->getID(), + $this->createItem($dropdown_classname, ['name' => 'Default option 2'])->getID(), + ]; + + $default_value = $option_ids; + $expected_value = $option_ids; + } else { + $option_id = $this->createItem($dropdown_classname, ['name' => 'Default option'])->getID(); + $default_value = (string) $option_id; + $expected_value = $option_id; + } + } + + $collector = $this->createItem( + MailCollector::class, + [ + 'name' => 'test-collector-' . $this->getUniqueString(), + 'is_active' => 1, + 'requester_field' => MailCollector::REQUESTER_FIELD_FROM, + 'mail_server' => 'imap.test.glpi.com', + 'server_type' => '/imap', + ], + ['mail_server', 'server_type'], + ); + + $sender_email = 'mailcollector-test-' . $this->getUniqueString() . '@test.glpi.com'; + $this->createItem(UserEmail::class, [ + 'users_id' => Session::getLoginUserID(), + 'is_default' => 1, + 'email' => $sender_email, + ]); + + $message = new Message([ + 'headers' => [ + 'From' => sprintf('Test requester <%s>', $sender_email), + 'To' => 'helpdesk@glpi.com', + 'Subject' => 'Ticket', + 'Message-Id' => '<' . uniqid('mailcollector-test-', true) . '@glpi-test.com>', + 'Date' => 'Mon, 01 Jan 2024 12:00:00 +0000', + ], + 'content' => 'This is a test email imported via the mail collector.', + ]); + + // No default value on the mandatory field + $tkt = $collector->buildTicket(1, $message, ['mailgates_id' => $collector->getID(), 'play_rules' => false]); + $tkt['entities_id'] = 0; + + $ticket = new Ticket(); + $ticket_id = $ticket->add($tkt); + $this->assertFalse($ticket_id, sprintf('Import must be blocked when the mandatory %s field has no value and no default.', $type)); + $this->hasSessionMessageThatContains( + __('Some mandatory fields are empty', 'fields'), + (string) ERROR, + ); + + $this->updateItem( + PluginFieldsField::class, + $field->getID(), + ['default_value' => $default_value], + $multiple ? ['default_value'] : [], + ); + + $tkt = $collector->buildTicket(2, $message, ['mailgates_id' => $collector->getID(), 'play_rules' => false]); + $tkt['entities_id'] = 0; + + $ticket = new Ticket(); + $ticket_id = $ticket->add($tkt); + $this->assertGreaterThan(0, $ticket_id, sprintf('Import must succeed once the mandatory %s field has a default value.', $type)); + + $classname = PluginFieldsContainer::getClassname(Ticket::class, $container->fields['name']); + $obj = getItemForItemtype($classname); + $obj->getFromDBByCrit([ + 'plugin_fields_containers_id' => $container->getID(), + 'items_id' => $ticket_id, + ]); + $container_ticket_fields_value = $obj->fields; + $stored_value = $multiple ? json_decode((string) $container_ticket_fields_value[$row_key], true) + : $container_ticket_fields_value[$row_key]; + $this->assertEquals($expected_value, $stored_value); + } } From 6cf337ddb51c2c063634ab5bedc0e124193eda05 Mon Sep 17 00:00:00 2001 From: MyuTsu Date: Tue, 1 Sep 2026 11:42:50 +0200 Subject: [PATCH 2/2] Update CHANGELOG.md --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f7ff05c1..1f5af770 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 default field values not being applied when fields are empty on creation + ## [1.24.4] - 2026-08-06 ### Fixed