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 ticket observers being able to edit and save additional fields they are not allowed to modify
- Fix `Yes/No` field values returned as strings instead of integers in search results
- Fix fatal error during plugin update caused by outdated generated dropdown classes
- Fix massive update of fields of a tab block not saved when the item has several tab blocks

## [1.25.0] - 2026-10-06

Expand Down
128 changes: 101 additions & 27 deletions inc/container.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -2008,6 +2008,56 @@ public static function findContainer($itemtype, $type = 'tab', $subtype = '')
return $id;
}

/**
* Find the active tab containers of the item's type having at least one field submitted in the item input
*
* @param CommonDBTM $item Item instance
*
* @return int[]
*/
private static function findSubmittedTabContainers(CommonDBTM $item): array
{
$container = new self();
$containers = $container->find([
'is_active' => 1,
'type' => ['!=', 'dom'],
]);

$c_ids = [];
foreach ($containers as $data) {
if (
in_array($item::class, PluginFieldsToolbox::decodeJSONItemtypes($data['itemtypes']))
&& Session::haveAccessToEntity($data['entities_id'], $data['is_recursive'])
) {
$c_ids[] = (int) $data['id'];
}
}

if ($c_ids === []) {
return [];
}

$fields = (new PluginFieldsField())->find([
'plugin_fields_containers_id' => $c_ids,
'is_active' => 1,
'type' => ['!=', 'header'],
], 'plugin_fields_containers_id');

$submitted = [];
foreach ($fields as $field) {
$input_keys = [
$field['name'],
sprintf('plugin_fields_%sdropdowns_id', $field['name']),
sprintf('items_id_%s', $field['name']),
];
if (array_intersect($input_keys, array_keys($item->input)) !== []) {
$submitted[] = (int) $field['plugin_fields_containers_id'];
}
}

return array_values(array_unique($submitted));
}

public static function findAllContainers($itemtype)
{
$condition = ['is_active' => 1];
Expand Down Expand Up @@ -2048,18 +2098,19 @@ public static function findAllContainers($itemtype)
public static function postItemAdd(CommonDBTM $item)
{
if (array_key_exists('_plugin_fields_data', $item->input)) {
$data = $item->input['_plugin_fields_data'];
$data['itemtype'] = $item::class;
$data['items_id'] = $item->getID();
$data['entities_id'] = $item->isEntityAssign() ? $item->getEntityID() : 0;
//update data
$container = new self();
if ($container->updateFieldsValues($data, $item::class, isset($_REQUEST['massiveaction']))) {
return true;
foreach (self::getSubmittedContainersData($item) as $data) {
$data['itemtype'] = $item::class;
$data['items_id'] = $item->getID();
$data['entities_id'] = $item->isEntityAssign() ? $item->getEntityID() : 0;
//update data
if (!$container->updateFieldsValues($data, $item::class, isset($_REQUEST['massiveaction']))) {
$item->input = [];
return $item;
}
}

$item->input = [];
return $item;
return true;
}

return true;
Expand Down Expand Up @@ -2092,26 +2143,43 @@ public static function preItemUpdate(CommonDBTM $item)
return true;
}

$data = $item->input['_plugin_fields_data'];
$data['itemtype'] = $item::class;
$data['entities_id'] = $item->isEntityAssign() ? $item->getEntityID() : 0;
//update data
$container = new self();
if (
count($data) === 0
|| $container->updateFieldsValues($data, $item::class, isset($_REQUEST['massiveaction']))
) {
$item->input['date_mod'] = $_SESSION['glpi_currenttime'];

return true;
foreach (self::getSubmittedContainersData($item) as $data) {
$data['itemtype'] = $item::class;
$data['entities_id'] = $item->isEntityAssign() ? $item->getEntityID() : 0;
//update data
if (!$container->updateFieldsValues($data, $item::class, isset($_REQUEST['massiveaction']))) {
return false;
}
}

return false;
$item->input['date_mod'] = $_SESSION['glpi_currenttime'];

return true;
}

return true;
}

/**
* Get the data to store for each submitted container
*
* @param CommonDBTM $item Item instance
*
* @return array<int, array<string, mixed>>
*/
private static function getSubmittedContainersData(CommonDBTM $item): array
{
$data = $item->input['_plugin_fields_data'];

// data of a single container
if (array_key_exists('plugin_fields_containers_id', $data)) {
return [$data];
}

return $data;
}

/**
* Pre item hook for add and update
* Validates and store plugin data in item object
Expand All @@ -2137,10 +2205,16 @@ public static function preItem(CommonDBTM $item)
// generic add/update: both the "dom" and "tab" containers can carry
// mandatory fields that must be enforced, even though only "dom"
// fields are actually submitted inline with the main form
$c_ids = array_filter(
[self::findContainer($item::class, 'dom'), self::findContainer($item::class, 'tab')],
// other tab containers whose fields are submitted (ex: massive action on a GLPI item
// dropdown field for which the core never displays the plugin field nor its c_id input)
$c_ids = array_values(array_unique(array_filter(
[
self::findContainer($item::class, 'dom'),
...self::findSubmittedTabContainers($item),
self::findContainer($item::class, 'tab'),
],
static fn($id) => $id !== false,
);
)));
}

if ($c_ids === []) {
Expand All @@ -2151,7 +2225,7 @@ public static function preItem(CommonDBTM $item)
$item->fields = $item->input;
}

$submitted_data = null;
$submitted_data = [];
foreach ($c_ids as $c_id) {
$loc_c = new PluginFieldsContainer();
$loc_c->getFromDB($c_id);
Expand Down Expand Up @@ -2186,11 +2260,11 @@ public static function preItem(CommonDBTM $item)
}

if ($result !== []) {
$submitted_data = $result;
$submitted_data[] = $result;
}
}

if ($submitted_data !== null) {
if ($submitted_data !== []) {
$item->input['_plugin_fields_data'] = $submitted_data;

return true;
Expand Down
Loading
Loading