From 55b9535f4b6fac30a8bf3b241121cb626d035eb0 Mon Sep 17 00:00:00 2001 From: MyuTsu Date: Fri, 8 Aug 2025 12:17:44 +0200 Subject: [PATCH 1/9] genericobject compatibility --- hook.php | 1 + inc/container.class.php | 52 +++++++++++++++++++++++++++++++++++ templates/container.class.tpl | 4 +-- 3 files changed, 55 insertions(+), 2 deletions(-) diff --git a/hook.php b/hook.php index 2835d88a..4c5ed68e 100644 --- a/hook.php +++ b/hook.php @@ -72,6 +72,7 @@ function plugin_fields_install() PluginFieldsStatusOverride::class, ]; + // First, install base data foreach ($classesToInstall as $class) { if (method_exists($class, 'installBaseData')) { diff --git a/inc/container.class.php b/inc/container.class.php index 9900305c..5a59b1f5 100644 --- a/inc/container.class.php +++ b/inc/container.class.php @@ -149,6 +149,58 @@ public static function installBaseData(Migration $migration, $version) $migration->migrationOneTable($table); } + // Get itemtypes from PluginGenericobject + $migration_genericobject_itemtype = []; + $result = $DB->request(['FROM' => 'glpi_plugin_genericobject_types']); + foreach ($result as $type) { + $migration_genericobject_itemtype[$type['itemtype']] = [ + 'genericobject_itemtype' => $type['itemtype'], + 'itemtype' => 'Glpi\\\\CustomAsset\\\\' . $type['name'] . "Asset", + 'genericobject_name' => $type['name'], + 'name' => $type['name'] . 'Asset', + ]; + } + + // Get containers with PluginGenericobject itemtype + $result = $DB->request([ + 'FROM' => $table, + 'WHERE' => [ + new Glpi\DBAL\QueryExpression( + $table . ".itemtypes LIKE '%PluginGenericobject%'", + ), + ], + ]); + + $container_class = new self(); + foreach ($result as $container) { + self::generateTemplate($container); + foreach(json_decode($container['itemtypes']) as $itemtype) { + $classname = self::getClassname($itemtype, $container["name"]); + $old_table = $classname::getTable(); + // Rename genericobject container table + if ( + $DB->tableExists($old_table) && + isset($migration_genericobject_itemtype[$itemtype]) && + strpos($old_table, 'glpi_plugin_fields_plugingenericobject' . $migration_genericobject_itemtype[$itemtype]['genericobject_name']) !== false + ) { + $new_table = str_replace('plugingenericobject' . $migration_genericobject_itemtype[$itemtype]['genericobject_name'], 'glpicustomasset' . strtolower($migration_genericobject_itemtype[$itemtype]['name']), $old_table); + $query = "RENAME TABLE `$old_table` TO `$new_table`"; + if (!$DB->doQuery($query)) { + throw new \RuntimeException('Error renaming table: ' . $DB->error()); + } + } + } + // Update old genericobject itemtypes in container + $map = array_column($migration_genericobject_itemtype, 'itemtype', 'genericobject_itemtype'); + $itemtypes = strtr($container['itemtypes'], $map); + $container_class->update( + [ + 'id' => $container['id'], + 'itemtypes' => $itemtypes, + ] + ); + } + return true; } diff --git a/templates/container.class.tpl b/templates/container.class.tpl index daa87766..5fb10fd2 100644 --- a/templates/container.class.tpl +++ b/templates/container.class.tpl @@ -51,7 +51,7 @@ class %%CLASSNAME%% extends PluginFieldsAbstractContainerInstance * This block ensures that the 'entities_id' field is created and populated if it * associated item type requires entity assignment */ - if (getItemForItemtype("%%ITEMTYPE%%")->isEntityAssign() && !$DB->fieldExists($table, 'entities_id')) { + if (getItemForItemtype('%%ITEMTYPE%%')->isEntityAssign() && !$DB->fieldExists($table, 'entities_id')) { $migration->addField($table, 'entities_id', 'fkey', ['after' => 'plugin_fields_containers_id']); $migration->addKey($table, 'entities_id'); $migration->executeMigration(); @@ -107,7 +107,7 @@ class %%CLASSNAME%% extends PluginFieldsAbstractContainerInstance * associated item type requires recursive assignment */ if ( - getItemForItemtype("%%ITEMTYPE%%")->maybeRecursive() + getItemForItemtype('%%ITEMTYPE%%')->maybeRecursive() && !$DB->fieldExists($table, 'is_recursive') && $DB->fieldExists($table, 'entities_id')) { $migration->addField($table, 'is_recursive', 'bool', ['after' => 'entities_id']); From ad67ec440717286d1e72fd370d0b2251ce80585f Mon Sep 17 00:00:00 2001 From: Rom1-B Date: Tue, 19 Aug 2025 11:51:42 +0200 Subject: [PATCH 2/9] review --- inc/container.class.php | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/inc/container.class.php b/inc/container.class.php index 5a59b1f5..99495f30 100644 --- a/inc/container.class.php +++ b/inc/container.class.php @@ -174,7 +174,7 @@ public static function installBaseData(Migration $migration, $version) $container_class = new self(); foreach ($result as $container) { self::generateTemplate($container); - foreach(json_decode($container['itemtypes']) as $itemtype) { + foreach (json_decode($container['itemtypes']) as $itemtype) { $classname = self::getClassname($itemtype, $container["name"]); $old_table = $classname::getTable(); // Rename genericobject container table @@ -184,10 +184,7 @@ public static function installBaseData(Migration $migration, $version) strpos($old_table, 'glpi_plugin_fields_plugingenericobject' . $migration_genericobject_itemtype[$itemtype]['genericobject_name']) !== false ) { $new_table = str_replace('plugingenericobject' . $migration_genericobject_itemtype[$itemtype]['genericobject_name'], 'glpicustomasset' . strtolower($migration_genericobject_itemtype[$itemtype]['name']), $old_table); - $query = "RENAME TABLE `$old_table` TO `$new_table`"; - if (!$DB->doQuery($query)) { - throw new \RuntimeException('Error renaming table: ' . $DB->error()); - } + $migration->renameTable($old_table, $new_table); } } // Update old genericobject itemtypes in container @@ -197,7 +194,7 @@ public static function installBaseData(Migration $migration, $version) [ 'id' => $container['id'], 'itemtypes' => $itemtypes, - ] + ], ); } From 95b911792ea3aea6ea21c8b75e09f43cfb44a3f6 Mon Sep 17 00:00:00 2001 From: Rom1-B Date: Tue, 19 Aug 2025 12:06:44 +0200 Subject: [PATCH 3/9] glpi_plugin_genericobject_types --- inc/container.class.php | 84 +++++++++++++++++++++-------------------- 1 file changed, 43 insertions(+), 41 deletions(-) diff --git a/inc/container.class.php b/inc/container.class.php index 99495f30..f279685e 100644 --- a/inc/container.class.php +++ b/inc/container.class.php @@ -151,51 +151,53 @@ public static function installBaseData(Migration $migration, $version) // Get itemtypes from PluginGenericobject $migration_genericobject_itemtype = []; - $result = $DB->request(['FROM' => 'glpi_plugin_genericobject_types']); - foreach ($result as $type) { - $migration_genericobject_itemtype[$type['itemtype']] = [ - 'genericobject_itemtype' => $type['itemtype'], - 'itemtype' => 'Glpi\\\\CustomAsset\\\\' . $type['name'] . "Asset", - 'genericobject_name' => $type['name'], - 'name' => $type['name'] . 'Asset', - ]; - } + if ($DB->tableExists('glpi_plugin_genericobject_types')) { + $result = $DB->request(['FROM' => 'glpi_plugin_genericobject_types']); + foreach ($result as $type) { + $migration_genericobject_itemtype[$type['itemtype']] = [ + 'genericobject_itemtype' => $type['itemtype'], + 'itemtype' => 'Glpi\\\\CustomAsset\\\\' . $type['name'] . "Asset", + 'genericobject_name' => $type['name'], + 'name' => $type['name'] . 'Asset', + ]; + } - // Get containers with PluginGenericobject itemtype - $result = $DB->request([ - 'FROM' => $table, - 'WHERE' => [ - new Glpi\DBAL\QueryExpression( - $table . ".itemtypes LIKE '%PluginGenericobject%'", - ), - ], - ]); + // Get containers with PluginGenericobject itemtype + $result = $DB->request([ + 'FROM' => $table, + 'WHERE' => [ + new Glpi\DBAL\QueryExpression( + $table . ".itemtypes LIKE '%PluginGenericobject%'", + ), + ], + ]); - $container_class = new self(); - foreach ($result as $container) { - self::generateTemplate($container); - foreach (json_decode($container['itemtypes']) as $itemtype) { - $classname = self::getClassname($itemtype, $container["name"]); - $old_table = $classname::getTable(); - // Rename genericobject container table - if ( - $DB->tableExists($old_table) && - isset($migration_genericobject_itemtype[$itemtype]) && - strpos($old_table, 'glpi_plugin_fields_plugingenericobject' . $migration_genericobject_itemtype[$itemtype]['genericobject_name']) !== false - ) { - $new_table = str_replace('plugingenericobject' . $migration_genericobject_itemtype[$itemtype]['genericobject_name'], 'glpicustomasset' . strtolower($migration_genericobject_itemtype[$itemtype]['name']), $old_table); - $migration->renameTable($old_table, $new_table); + $container_class = new self(); + foreach ($result as $container) { + self::generateTemplate($container); + foreach (json_decode($container['itemtypes']) as $itemtype) { + $classname = self::getClassname($itemtype, $container["name"]); + $old_table = $classname::getTable(); + // Rename genericobject container table + if ( + $DB->tableExists($old_table) && + isset($migration_genericobject_itemtype[$itemtype]) && + strpos($old_table, 'glpi_plugin_fields_plugingenericobject' . $migration_genericobject_itemtype[$itemtype]['genericobject_name']) !== false + ) { + $new_table = str_replace('plugingenericobject' . $migration_genericobject_itemtype[$itemtype]['genericobject_name'], 'glpicustomasset' . strtolower($migration_genericobject_itemtype[$itemtype]['name']), $old_table); + $migration->renameTable($old_table, $new_table); + } } + // Update old genericobject itemtypes in container + $map = array_column($migration_genericobject_itemtype, 'itemtype', 'genericobject_itemtype'); + $itemtypes = strtr($container['itemtypes'], $map); + $container_class->update( + [ + 'id' => $container['id'], + 'itemtypes' => $itemtypes, + ], + ); } - // Update old genericobject itemtypes in container - $map = array_column($migration_genericobject_itemtype, 'itemtype', 'genericobject_itemtype'); - $itemtypes = strtr($container['itemtypes'], $map); - $container_class->update( - [ - 'id' => $container['id'], - 'itemtypes' => $itemtypes, - ], - ); } return true; From 15952c2f512f2822ccc8b38bf6c593d7a350f45a Mon Sep 17 00:00:00 2001 From: Rom1-B Date: Thu, 21 Aug 2025 11:21:05 +0200 Subject: [PATCH 4/9] Check GenericObject version --- inc/container.class.php | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/inc/container.class.php b/inc/container.class.php index f279685e..f2f08481 100644 --- a/inc/container.class.php +++ b/inc/container.class.php @@ -28,8 +28,6 @@ * ------------------------------------------------------------------------- */ -use Glpi\Toolbox\Sanitizer; - class PluginFieldsContainer extends CommonDBTM { use Glpi\Features\Clonable; @@ -150,8 +148,19 @@ public static function installBaseData(Migration $migration, $version) } // Get itemtypes from PluginGenericobject - $migration_genericobject_itemtype = []; if ($DB->tableExists('glpi_plugin_genericobject_types')) { + // Check GenericObject version + $genericobject_info = Plugin::getInfo('genericobject'); + if ( + isset($genericobject_info['version']) && + version_compare($genericobject_info['version'], '2.14.14', '>=') + ) { + throw new \RuntimeException( + 'GenericObject plugin cannot be migrated. Please update it to the latest version.', + ); + } + + $migration_genericobject_itemtype = []; $result = $DB->request(['FROM' => 'glpi_plugin_genericobject_types']); foreach ($result as $type) { $migration_genericobject_itemtype[$type['itemtype']] = [ From bf9d76506d614cfe2d1ddd481ba0e4162045f3d3 Mon Sep 17 00:00:00 2001 From: Rom1-B Date: Thu, 21 Aug 2025 11:59:48 +0200 Subject: [PATCH 5/9] Check glpi_plugin_genericobject_types table --- inc/container.class.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/inc/container.class.php b/inc/container.class.php index f2f08481..2b5f4e1a 100644 --- a/inc/container.class.php +++ b/inc/container.class.php @@ -160,6 +160,13 @@ public static function installBaseData(Migration $migration, $version) ); } + // Check glpi_plugin_genericobject_types table + if (!$DB->fieldExists('glpi_plugin_genericobject_types', 'itemtype')) { + throw new \RuntimeException( + 'Integrity error on the glpi_plugin_genericobject_types table from the GenericObject plugin.', + ); + } + $migration_genericobject_itemtype = []; $result = $DB->request(['FROM' => 'glpi_plugin_genericobject_types']); foreach ($result as $type) { From cda106f074faafc5554cbd7e70db348aa624d476 Mon Sep 17 00:00:00 2001 From: Rom1-B Date: Tue, 26 Aug 2025 11:32:23 +0200 Subject: [PATCH 6/9] fix escape --- inc/container.class.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inc/container.class.php b/inc/container.class.php index 2b5f4e1a..915a25bd 100644 --- a/inc/container.class.php +++ b/inc/container.class.php @@ -172,7 +172,7 @@ public static function installBaseData(Migration $migration, $version) foreach ($result as $type) { $migration_genericobject_itemtype[$type['itemtype']] = [ 'genericobject_itemtype' => $type['itemtype'], - 'itemtype' => 'Glpi\\\\CustomAsset\\\\' . $type['name'] . "Asset", + 'itemtype' => 'Glpi\CustomAsset\\' . $type['name'] . 'Asset', 'genericobject_name' => $type['name'], 'name' => $type['name'] . 'Asset', ]; From 3af75548841a542cae77b9a21354982c1028f5b9 Mon Sep 17 00:00:00 2001 From: Rom1-B Date: Tue, 26 Aug 2025 12:00:01 +0200 Subject: [PATCH 7/9] fix check version --- inc/container.class.php | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/inc/container.class.php b/inc/container.class.php index 915a25bd..85a6a212 100644 --- a/inc/container.class.php +++ b/inc/container.class.php @@ -151,10 +151,7 @@ public static function installBaseData(Migration $migration, $version) if ($DB->tableExists('glpi_plugin_genericobject_types')) { // Check GenericObject version $genericobject_info = Plugin::getInfo('genericobject'); - if ( - isset($genericobject_info['version']) && - version_compare($genericobject_info['version'], '2.14.14', '>=') - ) { + if (version_compare($genericobject_info['version'] ?? '0', '2.14.14', '<')) { throw new \RuntimeException( 'GenericObject plugin cannot be migrated. Please update it to the latest version.', ); From e1862a772ec8a5f902783ed2faf6796dc8c9e19d Mon Sep 17 00:00:00 2001 From: MyuTsu Date: Tue, 23 Sep 2025 15:20:06 +0200 Subject: [PATCH 8/9] fix(customasset): handle escaped backslashes in json decode itemtypes --- inc/container.class.php | 17 ++++++++++------- inc/toolbox.class.php | 10 ++++++++++ 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/inc/container.class.php b/inc/container.class.php index 85a6a212..3ce9c05d 100644 --- a/inc/container.class.php +++ b/inc/container.class.php @@ -151,7 +151,7 @@ public static function installBaseData(Migration $migration, $version) if ($DB->tableExists('glpi_plugin_genericobject_types')) { // Check GenericObject version $genericobject_info = Plugin::getInfo('genericobject'); - if (version_compare($genericobject_info['version'] ?? '0', '2.14.14', '<')) { + if (version_compare($genericobject_info['version'] ?? '0', '3.0.0', '<')) { throw new \RuntimeException( 'GenericObject plugin cannot be migrated. Please update it to the latest version.', ); @@ -552,6 +552,7 @@ public static function getSpecificValueToDisplay($field, $values, array $options return $types[$values[$field]]; case 'itemtypes': $types = json_decode($values[$field]); + $types = PluginFieldsToolbox::decodeJSONItemtypes($values[$field]); $obj = ''; $count = count($types); $i = 1; @@ -714,7 +715,7 @@ public static function create($fields) return false; } - foreach (json_decode($fields['itemtypes']) as $itemtype) { + foreach (PluginFieldsToolbox::decodeJSONItemtypes($fields['itemtypes']) as $itemtype) { //install table for receive field $classname = self::getClassname($itemtype, $fields['name']); $classname::install(); @@ -724,7 +725,7 @@ public static function create($fields) public static function generateTemplate($fields) { $itemtypes = strlen($fields['itemtypes']) > 0 - ? json_decode($fields['itemtypes'], true) + ? PluginFieldsToolbox::decodeJSONItemtypes($fields['itemtypes'], true) : []; foreach ($itemtypes as $itemtype) { // prevent usage of plugin class if not loaded @@ -1166,7 +1167,7 @@ public static function getEntries($type = 'tab', $full = false): array continue; } - $jsonitemtypes = json_decode($item['itemtypes']); + $jsonitemtypes = PluginFieldsToolbox::decodeJSONItemtypes($item['itemtypes']); //show more info or not foreach ($jsonitemtypes as $v) { if ($full) { @@ -1206,8 +1207,10 @@ public static function getUsedItemtypes($type = 'all', $must_be_active = false) ]); foreach ($iterator as $data) { - $jsonitemtype = json_decode($data['itemtypes']); - $itemtypes = array_merge($itemtypes, $jsonitemtype); + $jsonitemtype = PluginFieldsToolbox::decodeJSONItemtypes($data['itemtypes']); + if (is_array($jsonitemtype)) { + $itemtypes = array_merge($itemtypes, $jsonitemtype); + } } return $itemtypes; @@ -1667,7 +1670,7 @@ public static function findContainer($itemtype, $type = 'tab', $subtype = '') } foreach ($itemtypes as $data) { - $dataitemtypes = json_decode($data['itemtypes']); + $dataitemtypes = PluginFieldsToolbox::decodeJSONItemtypes($data['itemtypes']); if (in_array($itemtype, $dataitemtypes) != false) { $id = $data['id']; } diff --git a/inc/toolbox.class.php b/inc/toolbox.class.php index 5e26de19..8f4e6da0 100644 --- a/inc/toolbox.class.php +++ b/inc/toolbox.class.php @@ -361,4 +361,14 @@ public static function getGlpiItemtypes(): array return $all_itemtypes; } + + public static function decodeJSONItemtypes(string $itemtypes, ?bool $associative = null) + { + $jsonitemtype = json_decode($itemtypes, $associative); + if ($jsonitemtype === null && json_last_error() !== JSON_ERROR_NONE) { + $fixed_json = str_replace('\\', '\\\\', $itemtypes); + $jsonitemtype = json_decode($fixed_json, $associative); + } + return $jsonitemtype; + } } From 8715a475e06daf8623652954f15f0b5d42503e87 Mon Sep 17 00:00:00 2001 From: "Romain B." <8530352+Rom1-B@users.noreply.github.com> Date: Wed, 24 Sep 2025 09:29:40 +0200 Subject: [PATCH 9/9] Apply suggestion from @Rom1-B --- inc/container.class.php | 1 - 1 file changed, 1 deletion(-) diff --git a/inc/container.class.php b/inc/container.class.php index 3ce9c05d..0f909524 100644 --- a/inc/container.class.php +++ b/inc/container.class.php @@ -551,7 +551,6 @@ public static function getSpecificValueToDisplay($field, $values, array $options return $types[$values[$field]]; case 'itemtypes': - $types = json_decode($values[$field]); $types = PluginFieldsToolbox::decodeJSONItemtypes($values[$field]); $obj = ''; $count = count($types);