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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
- Fix administrators losing access to a block's configuration after setting a profile to "no access" on that block.
- Fix dependency conflict with GLPI core by no longer vendoring symfony/deprecation-contracts and symfony/polyfill-ctype.
- Fix default field values not being applied when fields are empty on creation
- Fix mandatory fields blocking automated item creation

## [1.24.5] - 2026-09-11

Expand Down
31 changes: 29 additions & 2 deletions inc/container.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -1653,6 +1653,15 @@ public static function constructHistory(
}
}

private static function isMandatoryCheckBypassed(array $data): bool
{
return isCommandLine()
|| Session::isCron()
|| isAPI()
|| !empty($data['_auto_import'])
|| !empty($data['is_dynamic']);
}

/**
* check data inserted
* display a message when not ok
Expand Down Expand Up @@ -1739,9 +1748,9 @@ public static function validateValues($data, $itemtype, $massiveaction)
$field['itemtype'] = PluginFieldsField::getType();
$field['label'] = PluginFieldsLabelTranslation::getLabelFor($field);

// Check mandatory fields
if (
$field['mandatory'] == 1
!self::isMandatoryCheckBypassed($data)
&& $field['mandatory'] == 1
&& (
empty($value)
|| (($field['type'] === 'dropdown' || preg_match('/^dropdown-.+/i', (string) $field['type'])) && $value == 0)
Expand Down Expand Up @@ -2028,6 +2037,14 @@ private static function checkContainerMandatory(CommonDBTM $item, PluginFieldsCo

$status_field_name = PluginFieldsStatusOverride::getStatusFieldName($item::getType());
$data = ['plugin_fields_containers_id' => $c_id];
if ($item->input['_auto_import'] ?? false) {
$data['_auto_import'] = true;
}

if (!empty($item->input['is_dynamic'])) {
$data['is_dynamic'] = true;
}

if (array_key_exists($status_field_name, $item->input) && $item->input[$status_field_name] !== '') {
$data[$status_field_name] = (int) $item->input[$status_field_name];
} elseif (array_key_exists($status_field_name, $item->fields) && $item->fields[$status_field_name] !== '') {
Expand Down Expand Up @@ -2094,6 +2111,16 @@ private static function populateData($c_id, CommonDBTM $item)
$data['items_id'] = $item->getID();
}

// Carry over the "automated import" markers so mandatory fields can be relaxed
// for items created without a human filling a form.
if (!empty($item->input['_auto_import'])) {
$data['_auto_import'] = true;
}

if (!empty($item->input['is_dynamic'])) {
$data['is_dynamic'] = true;
}

// Add status so it can be used with status overrides
$status_field_name = PluginFieldsStatusOverride::getStatusFieldName($item->getType());
$data[$status_field_name] = null;
Expand Down
5 changes: 5 additions & 0 deletions tests/Units/ContainerItemUpdateTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -65,10 +65,14 @@ final class ContainerItemUpdateTest extends DbTestCase
public function setUp(): void
{
GLPITestCase::setUp();

$GLOBALS['GLPI_IS_COMMAND_LINE'] = false;
}

public function tearDown(): void
{
unset($GLOBALS['GLPI_IS_COMMAND_LINE']);

global $DB;
$DB->setMustUnsanitizeData(false); // Be sure to switch back to disabled unsanitization.

Expand Down Expand Up @@ -473,6 +477,7 @@ public function testUpdateIsBlockedWhenMandatoryTabFieldWasNeverFilled(): void
'id' => $problem->getID(),
'name' => 'Renamed while tab field still empty',
]);

$this->assertFalse($updated, 'Update must be blocked while a mandatory tab field is empty.');
$this->hasSessionMessageThatContains(
__('Some mandatory fields are empty', 'fields'),
Expand Down
21 changes: 7 additions & 14 deletions tests/Units/ContainerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,14 @@ public function setUp(): void
{
GLPITestCase::setUp();
$this->login();

$GLOBALS['GLPI_IS_COMMAND_LINE'] = false;
}

public function tearDown(): void
{
unset($GLOBALS['GLPI_IS_COMMAND_LINE']);

$this->tearDownFieldTest();
GLPITestCase::tearDown();
}
Expand Down Expand Up @@ -245,17 +249,9 @@ public function testMailCollectorImportRespectsMandatoryFieldDefaultValue(
'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->createItem(Ticket::class, $tkt, ['users_id', 'itemtype']);

$this->updateItem(
PluginFieldsField::class,
Expand All @@ -266,16 +262,13 @@ public function testMailCollectorImportRespectsMandatoryFieldDefaultValue(

$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));
$ticket = $this->createItem(Ticket::class, $tkt, ['users_id', 'itemtype']);

$classname = PluginFieldsContainer::getClassname(Ticket::class, $container->fields['name']);
$obj = getItemForItemtype($classname);
$obj->getFromDBByCrit([
'plugin_fields_containers_id' => $container->getID(),
'items_id' => $ticket_id,
'items_id' => $ticket->getID(),
]);
$container_ticket_fields_value = $obj->fields;
$stored_value = $multiple ? json_decode((string) $container_ticket_fields_value[$row_key], true)
Expand Down
Loading