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
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
16 changes: 16 additions & 0 deletions inc/container.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}
}

Expand All @@ -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;
}
}
}

Expand Down
40 changes: 27 additions & 13 deletions inc/field.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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)) {
Expand Down
4 changes: 2 additions & 2 deletions tests/FieldTestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
142 changes: 142 additions & 0 deletions tests/Units/ContainerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down Expand Up @@ -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);
}
}