diff --git a/CHANGELOG.md b/CHANGELOG.md index 66778cc17d6..7887f6c3422 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -65,6 +65,7 @@ The present file will list all changes made to the project; according to the - The `Computer_Item` class has been replaced by the `\Glpi\Asset\Asset_PeripheralAsset` class. - List of network ports in a VLAN form now shows the NetworkPort link in a breadcrumb manner (MyServer > eth0 where MyServer is a link to the computer and eth0 is a link to the port). - Running `front/cron.php` or `bin/console` will attempt to check and block execution if running as root. +- `Group` and `Group in charge` fields for assets may now contain multiple groups. ### Deprecated - Survey URL tags `TICKETCATEGORY_ID` and `TICKETCATEGORY_NAME` are deprecated and replaced by `ITILCATEGORY_ID` and `ITILCATEGORY_NAME` respectively. @@ -149,6 +150,11 @@ The present file will list all changes made to the project; according to the - Usage of `ajax/dropdownValidator.php` with the `users_id_validate` parameter is no longer supported. Use `items_id_target` instead. - `Glpi\Dashboard\Filters\AbstractFilter::field()` method has been made protected. - Usage of `CommonITILValidation::dropdownValidator()` with the `name` and `users_id_validate` options are no longer supported. Use `prefix` and `itemtype_target`/`items_id_target` respectively instead. +- Any class added to `$CFG_GLPI['directconnect_types']` must now use the `Glpi\Features\AssignableItem` trait as multi-group support is required. +- For assets, `groups_id` and `groups_id_tech` fields were changed from integers to arrays and are loaded into the `fields` array after `getFromDB`/`getEmpty`. + If reading directly from the DB, you need to query the new linking table `glpi_groups_items`. +- `Group::getDataItems()` signature changed. The two first parameters `$types` and `$field` were replaced + by a unique boolean `$tech` parameter that is used to compute the `$types` and `$field` values automatically. #### Deprecated - Usage of `MAIL_SMTPSSL` and `MAIL_SMTPTLS` constants. @@ -161,6 +167,7 @@ The present file will list all changes made to the project; according to the - Defining "users_id_validate" field without defining "itemtype_target"/"items_id_target" in "CommonITILValidation". - Usage of `name` and `users_id_validate` options in `CommonITILValidation::dropdownValidator()`. - Usage of `verbatim_value` Twig filter. +- `linkuser_types`, `linkgroup_types`, `linkuser_tech_types`, `linkgroup_tech_types` configuration entries have been merged in a unique `assignable_types` configuration entry. - `Auth::getErr()` - `AuthLDAP::dropdownUserDeletedActions()` - `AuthLDAP::getOptions()` diff --git a/inc/define.php b/inc/define.php index 19c6af1d251..6df5d85adb9 100644 --- a/inc/define.php +++ b/inc/define.php @@ -259,14 +259,36 @@ 'Peripheral', 'Phone', 'Printer', 'Software', 'Rack' ]; -// FIXME: Merge these configurations -$CFG_GLPI["linkuser_types"] = ['Computer', 'CartridgeItem', 'ConsumableItem', 'Monitor', 'NetworkEquipment', - 'Peripheral', 'Phone', 'Printer', 'Software', - 'SoftwareLicense', 'Certificate', 'Appliance', 'DatabaseInstance', 'Item_DeviceSimcard', 'Line' -]; -$CFG_GLPI["linkgroup_types"] = $CFG_GLPI["linkuser_types"]; -$CFG_GLPI["linkuser_tech_types"] = $CFG_GLPI["linkuser_types"]; -$CFG_GLPI["linkgroup_tech_types"] = $CFG_GLPI["linkuser_types"]; +$CFG_GLPI["assignable_types"] = [ + 'Appliance', + 'Cable', + 'CartridgeItem', + 'Certificate', + 'Cluster', + 'Computer', + 'ConsumableItem', + 'DatabaseInstance', + 'Domain', + 'DomainRecord', + 'Enclosure', + 'Item_DeviceSimcard', + 'Line', + 'Monitor', + 'NetworkEquipment', + 'PassiveDCEquipment', + 'PDU', + 'Peripheral', + 'Phone', + 'Printer', + 'Rack', + 'Software', + 'SoftwareLicense', + 'Unmanaged', +]; +$CFG_GLPI["linkuser_types"] = $CFG_GLPI["assignable_types"]; +$CFG_GLPI["linkgroup_types"] = $CFG_GLPI["assignable_types"]; +$CFG_GLPI["linkuser_tech_types"] = $CFG_GLPI["assignable_types"]; +$CFG_GLPI["linkgroup_tech_types"] = $CFG_GLPI["assignable_types"]; $CFG_GLPI["location_types"] = ['Budget', 'CartridgeItem', 'ConsumableItem', 'Computer', 'Monitor', "Glpi\\Socket", diff --git a/inc/relation.constant.php b/inc/relation.constant.php index a4ba6174dfb..8b5ea137f4f 100644 --- a/inc/relation.constant.php +++ b/inc/relation.constant.php @@ -715,99 +715,22 @@ ], 'glpi_groups' => [ - 'glpi_appliances' => [ - 'groups_id_tech', - 'groups_id', - ], - 'glpi_assets_assets' => [ - 'groups_id_tech', - 'groups_id', - ], - 'glpi_cartridgeitems' => [ - 'groups_id_tech', - 'groups_id', - ], - 'glpi_certificates' => [ - 'groups_id_tech', - 'groups_id', - ], '_glpi_changes_groups' => 'groups_id', 'glpi_changetasks' => 'groups_id_tech', - 'glpi_clusters' => 'groups_id_tech', - 'glpi_computers' => [ - 'groups_id_tech', - 'groups_id', - ], - 'glpi_consumableitems' => [ - 'groups_id_tech', - 'groups_id', - ], - 'glpi_databaseinstances' => [ - 'groups_id_tech', - 'groups_id', - ], - 'glpi_domains' => 'groups_id_tech', - 'glpi_domainrecords' => 'groups_id_tech', - 'glpi_enclosures' => 'groups_id_tech', 'glpi_groups' => 'groups_id', + '_glpi_groups_items' => 'groups_id', '_glpi_groups_knowbaseitems' => 'groups_id', '_glpi_groups_problems' => 'groups_id', '_glpi_groups_reminders' => 'groups_id', '_glpi_groups_rssfeeds' => 'groups_id', '_glpi_groups_tickets' => 'groups_id', '_glpi_groups_users' => 'groups_id', - 'glpi_items_devicesimcards' => [ - 'groups_id_tech', - 'groups_id', - ], 'glpi_itilcategories' => 'groups_id', - 'glpi_lines' => [ - 'groups_id_tech', - 'groups_id', - ], - 'glpi_monitors' => [ - 'groups_id_tech', - 'groups_id', - ], - 'glpi_networkequipments' => [ - 'groups_id_tech', - 'groups_id', - ], - 'glpi_passivedcequipments' => 'groups_id_tech', - 'glpi_pdus' => 'groups_id_tech', - 'glpi_peripherals' => [ - 'groups_id_tech', - 'groups_id', - ], 'glpi_planningexternalevents' => 'groups_id', - 'glpi_phones' => [ - 'groups_id_tech', - 'groups_id', - ], - 'glpi_printers' => [ - 'groups_id_tech', - 'groups_id', - ], 'glpi_problemtasks' => 'groups_id_tech', 'glpi_projects' => 'groups_id', - 'glpi_racks' => [ - 'groups_id_tech', - 'groups_id', - ], - 'glpi_softwarelicenses' => [ - 'groups_id_tech', - 'groups_id', - ], - 'glpi_softwares' => [ - 'groups_id_tech', - 'groups_id', - ], 'glpi_tasktemplates' => 'groups_id_tech', 'glpi_tickettasks' => 'groups_id_tech', - 'glpi_unmanageds' => [ - 'groups_id_tech', - 'groups_id', - ], 'glpi_users' => 'groups_id', 'glpi_itilvalidationtemplates_targets' => 'groups_id', ], @@ -1539,7 +1462,10 @@ 'users_id_tech', 'users_id', ], - 'glpi_cables' => 'users_id_tech', + 'glpi_cables' => [ + 'users_id_tech', + 'users_id', + ], 'glpi_cartridgeitems' => [ 'users_id_tech', 'users_id', @@ -1562,7 +1488,10 @@ 'users_id', 'users_id_validate', ], - 'glpi_clusters' => 'users_id_tech', + 'glpi_clusters' => [ + 'users_id_tech', + 'users_id', + ], 'glpi_computers' => [ 'users_id_tech', 'users_id', @@ -1578,11 +1507,20 @@ 'users_id', ], '_glpi_displaypreferences' => 'users_id', - 'glpi_domains' => 'users_id_tech', - 'glpi_domainrecords' => 'users_id_tech', + 'glpi_domains' => [ + 'users_id_tech', + 'users_id', + ], + 'glpi_domainrecords' => [ + 'users_id_tech', + 'users_id', + ], 'glpi_documents' => 'users_id', 'glpi_documents_items' => 'users_id', - 'glpi_enclosures' => 'users_id_tech', + 'glpi_enclosures' => [ + 'users_id_tech', + 'users_id', + ], 'glpi_forms_answerssets' => 'users_id', '_glpi_groups_users' => 'users_id', 'glpi_items_devicesimcards' => [ @@ -1623,8 +1561,14 @@ ], 'glpi_notimportedemails' => 'users_id', '_glpi_objectlocks' => 'users_id', - 'glpi_passivedcequipments' => 'users_id_tech', - 'glpi_pdus' => 'users_id_tech', + 'glpi_passivedcequipments' => [ + 'users_id_tech', + 'users_id', + ], + 'glpi_pdus' => [ + 'users_id_tech', + 'users_id', + ], 'glpi_peripherals' => [ 'users_id_tech', 'users_id', @@ -1871,3 +1815,12 @@ $define_mapping_entry($source_table, $target_table_key); $RELATION[$source_table][$target_table_key][] = ['itemtype_asset', 'items_id_asset']; } + +// Multiple groups assignments +$assignable_itemtypes = $CFG_GLPI['assignable_types']; +foreach ($assignable_itemtypes as $assignable_itemtype) { + $source_table_key = $assignable_itemtype::getTable(); + + $define_mapping_entry($source_table_key, '_glpi_groups_items'); + $RELATION[$source_table_key]['_glpi_groups_items'][] = ['itemtype', 'items_id']; +} diff --git a/install/migrations/update_10.0.x_to_11.0.0/assets.php b/install/migrations/update_10.0.x_to_11.0.0/assets.php index 59e2082d0ed..7b7b9e8a8f6 100644 --- a/install/migrations/update_10.0.x_to_11.0.0/assets.php +++ b/install/migrations/update_10.0.x_to_11.0.0/assets.php @@ -86,9 +86,7 @@ `contact` varchar(255) DEFAULT NULL, `contact_num` varchar(255) DEFAULT NULL, `users_id` int {$default_key_sign} NOT NULL DEFAULT '0', - `groups_id` int {$default_key_sign} NOT NULL DEFAULT '0', `users_id_tech` int {$default_key_sign} NOT NULL DEFAULT '0', - `groups_id_tech` int {$default_key_sign} NOT NULL DEFAULT '0', `locations_id` int {$default_key_sign} NOT NULL DEFAULT '0', `manufacturers_id` int {$default_key_sign} NOT NULL DEFAULT '0', `states_id` int {$default_key_sign} NOT NULL DEFAULT '0', @@ -105,9 +103,7 @@ KEY `assets_assettypes_id` (`assets_assettypes_id`), KEY `name` (`name`), KEY `users_id` (`users_id`), - KEY `groups_id` (`groups_id`), KEY `users_id_tech` (`users_id_tech`), - KEY `groups_id_tech` (`groups_id_tech`), KEY `locations_id` (`locations_id`), KEY `manufacturers_id` (`manufacturers_id`), KEY `states_id` (`states_id`), @@ -128,6 +124,10 @@ $migration->addField('glpi_assets_assets', 'is_template', 'bool'); $migration->addKey('glpi_assets_assets', 'is_template'); $migration->addField('glpi_assets_assets', 'template_name', 'string'); + $migration->dropKey('glpi_assets_assets', 'groups_id'); + $migration->dropField('glpi_assets_assets', 'groups_id'); + $migration->dropKey('glpi_assets_assets', 'groups_id_tech'); + $migration->dropField('glpi_assets_assets', 'groups_id_tech'); } if (!$DB->tableExists('glpi_assets_assetmodels')) { @@ -212,41 +212,3 @@ ]); } } - -// Add missing fields on assignable items -$migration->addField('glpi_cartridgeitems', 'users_id', 'fkey'); -$migration->addKey('glpi_cartridgeitems', 'users_id'); -$migration->addField('glpi_cartridgeitems', 'groups_id', 'fkey'); -$migration->addKey('glpi_cartridgeitems', 'groups_id'); - -$migration->addField('glpi_consumableitems', 'users_id', 'fkey'); -$migration->addKey('glpi_consumableitems', 'users_id'); -$migration->addField('glpi_consumableitems', 'groups_id', 'fkey'); -$migration->addKey('glpi_consumableitems', 'groups_id'); - -$migration->addField('glpi_databaseinstances', 'users_id', 'fkey'); -$migration->addKey('glpi_databaseinstances', 'users_id'); -$migration->addField('glpi_databaseinstances', 'groups_id', 'fkey'); -$migration->addKey('glpi_databaseinstances', 'groups_id'); - -$migration->addField('glpi_items_devicesimcards', 'users_id_tech', 'fkey'); -$migration->addKey('glpi_items_devicesimcards', 'users_id_tech'); -$migration->addField('glpi_items_devicesimcards', 'groups_id_tech', 'fkey'); -$migration->addKey('glpi_items_devicesimcards', 'groups_id_tech'); - -$migration->addField('glpi_lines', 'users_id_tech', 'fkey'); -$migration->addKey('glpi_lines', 'users_id_tech'); -$migration->addField('glpi_lines', 'groups_id_tech', 'fkey'); -$migration->addKey('glpi_lines', 'groups_id_tech'); - -// Add assignable assets rights -$assignable_asset_rights = [ - 'computer', 'monitor', 'software', 'networking', 'printer', - 'cartridge', 'consumable', 'phone', 'peripheral' -]; -foreach ($assignable_asset_rights as $rightname) { - $migration->addRight($rightname, READ_ASSIGNED, [$rightname => READ]); - $migration->addRight($rightname, UPDATE_ASSIGNED, [$rightname => UPDATE]); - $migration->addRight($rightname, READ_OWNED, [$rightname => READ]); - $migration->addRight($rightname, UPDATE_OWNED, [$rightname => UPDATE]); -} diff --git a/install/migrations/update_10.0.x_to_11.0.0/assignable_items.php b/install/migrations/update_10.0.x_to_11.0.0/assignable_items.php new file mode 100644 index 00000000000..0105112e0a7 --- /dev/null +++ b/install/migrations/update_10.0.x_to_11.0.0/assignable_items.php @@ -0,0 +1,229 @@ +. + * + * --------------------------------------------------------------------- + */ + +/** + * @var array $ADDTODISPLAYPREF + * @var DB $DB + * @var Migration $migration + */ + +$default_charset = DBConnection::getDefaultCharset(); +$default_collation = DBConnection::getDefaultCollation(); +$default_key_sign = DBConnection::getDefaultPrimaryKeySignOption(); + +// Add assignable assets rights +$assignable_asset_rights = [ + 'computer', 'monitor', 'software', 'networking', 'printer', + 'cartridge', 'consumable', 'phone', 'peripheral' +]; +foreach ($assignable_asset_rights as $rightname) { + $migration->addRight($rightname, READ_ASSIGNED, [$rightname => READ]); + $migration->addRight($rightname, UPDATE_ASSIGNED, [$rightname => UPDATE]); + $migration->addRight($rightname, READ_OWNED, [$rightname => READ]); + $migration->addRight($rightname, UPDATE_OWNED, [$rightname => UPDATE]); +} + +$assignable_itemtypes = [ + 'Appliance' => [ + 'table' => 'glpi_appliances', + 'rightname' => 'appliance' + ], + 'Cable' => [ + 'table' => 'glpi_cables', + 'rightname' => 'cable_management' + ], + 'CartridgeItem' => [ + 'table' => 'glpi_cartridgeitems', + 'rightname' => 'cartridge' + ], + 'Certificate' => [ + 'table' => 'glpi_certificates', + 'rightname' => 'certificate' + ], + 'Cluster' => [ + 'table' => 'glpi_clusters', + 'rightname' => 'cluster' + ], + 'Computer' => [ + 'table' => 'glpi_computers', + 'rightname' => 'computer' + ], + 'ConsumableItem' => [ + 'table' => 'glpi_consumableitems', + 'rightname' => 'consumable' + ], + 'DatabaseInstance' => [ + 'table' => 'glpi_databaseinstances', + 'rightname' => 'databaseinstance' + ], + 'Domain' => [ + 'table' => 'glpi_domains', + 'rightname' => 'domain' + ], + 'DomainRecord' => [ + 'table' => 'glpi_domainrecords', + 'rightname' => 'domain' + ], + 'Enclosure' => [ + 'table' => 'glpi_enclosures', + 'rightname' => 'datacenter' + ], + 'Item_DeviceSimcard' => [ + 'table' => 'glpi_items_devicesimcards', + 'rightname' => 'device' + ], + 'Line' => [ + 'table' => 'glpi_lines', + 'rightname' => 'line' + ], + 'Monitor' => [ + 'table' => 'glpi_monitors', + 'rightname' => 'monitor' + ], + 'NetworkEquipment' => [ + 'table' => 'glpi_networkequipments', + 'rightname' => 'networking' + ], + 'PassiveDCEquipment' => [ + 'table' => 'glpi_passivedcequipments', + 'rightname' => 'datacenter' + ], + 'PDU' => [ + 'table' => 'glpi_pdus', + 'rightname' => 'datacenter' + ], + 'Peripheral' => [ + 'table' => 'glpi_peripherals', + 'rightname' => 'peripheral' + ], + 'Phone' => [ + 'table' => 'glpi_phones', + 'rightname' => 'phone' + ], + 'Printer' => [ + 'table' => 'glpi_printers', + 'rightname' => 'printer' + ], + 'Rack' => [ + 'table' => 'glpi_racks', + 'rightname' => 'datacenter' + ], + 'Software' => [ + 'table' => 'glpi_softwares', + 'rightname' => 'software' + ], + 'SoftwareLicense' => [ + 'table' => 'glpi_softwarelicenses', + 'rightname' => 'license' + ], + 'Unmanaged' => [ + 'table' => 'glpi_unmanageds', + 'rightname' => 'unmanaged' + ], +]; + +if ($DB->tableExists('glpi_groups_assets') && !$DB->tableExists('glpi_groups_items')) { + // dev migration + // TODO Delete before GLPI 11.0 release + $migration->renameTable('glpi_groups_assets', 'glpi_groups_items'); +} + +if (!$DB->tableExists('glpi_groups_items')) { + $query = <<doQueryOrDie($query); +} + +foreach ($assignable_itemtypes as $itemtype => $specs) { + $itemtype_table = $specs['table']; + $itemtype_rightname = $specs['rightname']; + + $migration->addRight($itemtype_rightname, READ_ASSIGNED, [$itemtype_rightname => READ]); + $migration->addRight($itemtype_rightname, UPDATE_ASSIGNED, [$itemtype_rightname => UPDATE]); + + // Add missing `users_id`/`users_id_tech` fields on assignable items + $migration->addField($itemtype_table, 'users_id', 'fkey'); + $migration->addKey($itemtype_table, 'users_id'); + $migration->addField($itemtype_table, 'users_id_tech', 'fkey'); + $migration->addKey($itemtype_table, 'users_id_tech'); + + // move groups to the new link table + if ($DB->fieldExists($itemtype_table, 'groups_id')) { + $DB->insert('glpi_groups_items', new \Glpi\DBAL\QuerySubQuery([ + 'SELECT' => [ + new \Glpi\DBAL\QueryExpression('NULL', 'id'), + 'groups_id', + new \Glpi\DBAL\QueryExpression($DB::quoteValue($itemtype), 'itemtype'), + 'id AS items_id', + new \Glpi\DBAL\QueryExpression('1', 'type'), + ], + 'FROM' => $itemtype_table, + 'WHERE' => [ + 'groups_id' => ['>', 0] + ] + ])); + } + if ($DB->fieldExists($itemtype_table, 'groups_id_tech')) { + $DB->insert('glpi_groups_items', new \Glpi\DBAL\QuerySubQuery([ + 'SELECT' => [ + new \Glpi\DBAL\QueryExpression('NULL', 'id'), + 'groups_id', + new \Glpi\DBAL\QueryExpression($DB::quoteValue($itemtype), 'itemtype'), + 'id AS items_id', + new \Glpi\DBAL\QueryExpression('2', 'type'), + ], + 'FROM' => $itemtype_table, + 'WHERE' => [ + 'groups_id_tech' => ['>', 0] + ] + ])); + } + + $migration->dropKey($itemtype_table, 'groups_id'); + $migration->dropKey($itemtype_table, 'groups_id_tech'); + $migration->dropField($itemtype_table, 'groups_id'); + $migration->dropField($itemtype_table, 'groups_id_tech'); +} diff --git a/install/migrations/update_10.0.x_to_11.0.0/racks.php b/install/migrations/update_10.0.x_to_11.0.0/racks.php deleted file mode 100644 index 2a2a2a25c48..00000000000 --- a/install/migrations/update_10.0.x_to_11.0.0/racks.php +++ /dev/null @@ -1,67 +0,0 @@ -. - * - * --------------------------------------------------------------------- - */ - -/** - * @var \Migration $migration - * @var \DBmysql $DB - */ - -$default_charset = DBConnection::getDefaultCharset(); -$default_collation = DBConnection::getDefaultCollation(); -$default_key_sign = DBConnection::getDefaultPrimaryKeySignOption(); - -if (!$DB->fieldExists('glpi_racks', 'users_id')) { - $migration->addField( - 'glpi_racks', - 'users_id', - "int {$default_key_sign} NOT NULL DEFAULT '0'", - [ - 'after' => 'states_id' - ] - ); - $migration->addKey('glpi_racks', 'users_id'); -} - -if (!$DB->fieldExists('glpi_racks', 'groups_id')) { - $migration->addField( - 'glpi_racks', - 'groups_id', - "int {$default_key_sign} NOT NULL DEFAULT '0'", - [ - 'after' => 'users_id' - ] - ); - $migration->addKey('glpi_racks', 'groups_id'); -} diff --git a/install/mysql/glpi-empty.sql b/install/mysql/glpi-empty.sql index 05e5923f35d..9c314cb5dc0 100644 --- a/install/mysql/glpi-empty.sql +++ b/install/mysql/glpi-empty.sql @@ -435,8 +435,6 @@ CREATE TABLE `glpi_cartridgeitems` ( `manufacturers_id` int unsigned NOT NULL DEFAULT '0', `users_id` int unsigned NOT NULL DEFAULT '0', `users_id_tech` int unsigned NOT NULL DEFAULT '0', - `groups_id` int unsigned NOT NULL DEFAULT '0', - `groups_id_tech` int unsigned NOT NULL DEFAULT '0', `is_deleted` tinyint NOT NULL DEFAULT '0', `comment` text, `alarm_threshold` int NOT NULL DEFAULT '10', @@ -455,8 +453,6 @@ CREATE TABLE `glpi_cartridgeitems` ( KEY `cartridgeitemtypes_id` (`cartridgeitemtypes_id`), KEY `is_deleted` (`is_deleted`), KEY `alarm_threshold` (`alarm_threshold`), - KEY `groups_id` (`groups_id`), - KEY `groups_id_tech` (`groups_id_tech`), KEY `date_mod` (`date_mod`), KEY `date_creation` (`date_creation`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; @@ -545,13 +541,11 @@ CREATE TABLE `glpi_certificates` ( `dns_name` varchar(255) DEFAULT NULL, `dns_suffix` varchar(255) DEFAULT NULL, `users_id_tech` int unsigned NOT NULL DEFAULT '0' COMMENT 'RELATION to glpi_users (id)', - `groups_id_tech` int unsigned NOT NULL DEFAULT '0' COMMENT 'RELATION to glpi_groups (id)', `locations_id` int unsigned NOT NULL DEFAULT '0' COMMENT 'RELATION to glpi_locations (id)', `manufacturers_id` int unsigned NOT NULL DEFAULT '0' COMMENT 'RELATION to glpi_manufacturers (id)', `contact` varchar(255) DEFAULT NULL, `contact_num` varchar(255) DEFAULT NULL, `users_id` int unsigned NOT NULL DEFAULT '0', - `groups_id` int unsigned NOT NULL DEFAULT '0', `is_autosign` tinyint NOT NULL DEFAULT '0', `date_expiration` date DEFAULT NULL, `states_id` int unsigned NOT NULL DEFAULT '0' COMMENT 'RELATION to states (id)', @@ -568,8 +562,6 @@ CREATE TABLE `glpi_certificates` ( KEY `is_deleted` (`is_deleted`), KEY `certificatetypes_id` (`certificatetypes_id`), KEY `users_id_tech` (`users_id_tech`), - KEY `groups_id_tech` (`groups_id_tech`), - KEY `groups_id` (`groups_id`), KEY `users_id` (`users_id`), KEY `locations_id` (`locations_id`), KEY `manufacturers_id` (`manufacturers_id`), @@ -981,7 +973,6 @@ CREATE TABLE `glpi_computers` ( `contact` varchar(255) DEFAULT NULL, `contact_num` varchar(255) DEFAULT NULL, `users_id_tech` int unsigned NOT NULL DEFAULT '0', - `groups_id_tech` int unsigned NOT NULL DEFAULT '0', `comment` text, `date_mod` timestamp NULL DEFAULT NULL, `autoupdatesystems_id` int unsigned NOT NULL DEFAULT '0', @@ -995,7 +986,6 @@ CREATE TABLE `glpi_computers` ( `is_deleted` tinyint NOT NULL DEFAULT '0', `is_dynamic` tinyint NOT NULL DEFAULT '0', `users_id` int unsigned NOT NULL DEFAULT '0', - `groups_id` int unsigned NOT NULL DEFAULT '0', `states_id` int unsigned NOT NULL DEFAULT '0', `ticket_tco` decimal(20,4) DEFAULT '0.0000', `uuid` varchar(255) DEFAULT NULL, @@ -1010,7 +1000,6 @@ CREATE TABLE `glpi_computers` ( KEY `autoupdatesystems_id` (`autoupdatesystems_id`), KEY `entities_id` (`entities_id`), KEY `manufacturers_id` (`manufacturers_id`), - KEY `groups_id` (`groups_id`), KEY `users_id` (`users_id`), KEY `locations_id` (`locations_id`), KEY `computermodels_id` (`computermodels_id`), @@ -1019,7 +1008,6 @@ CREATE TABLE `glpi_computers` ( KEY `users_id_tech` (`users_id_tech`), KEY `computertypes_id` (`computertypes_id`), KEY `is_deleted` (`is_deleted`), - KEY `groups_id_tech` (`groups_id_tech`), KEY `is_dynamic` (`is_dynamic`), KEY `serial` (`serial`), KEY `otherserial` (`otherserial`), @@ -1327,8 +1315,6 @@ CREATE TABLE `glpi_consumableitems` ( `manufacturers_id` int unsigned NOT NULL DEFAULT '0', `users_id` int unsigned NOT NULL DEFAULT '0', `users_id_tech` int unsigned NOT NULL DEFAULT '0', - `groups_id` int unsigned NOT NULL DEFAULT '0', - `groups_id_tech` int unsigned NOT NULL DEFAULT '0', `is_deleted` tinyint NOT NULL DEFAULT '0', `comment` text, `alarm_threshold` int NOT NULL DEFAULT '10', @@ -1348,8 +1334,6 @@ CREATE TABLE `glpi_consumableitems` ( KEY `consumableitemtypes_id` (`consumableitemtypes_id`), KEY `is_deleted` (`is_deleted`), KEY `alarm_threshold` (`alarm_threshold`), - KEY `groups_id` (`groups_id`), - KEY `groups_id_tech` (`groups_id_tech`), KEY `date_mod` (`date_mod`), KEY `date_creation` (`date_creation`), KEY `otherserial` (`otherserial`) @@ -2472,8 +2456,6 @@ CREATE TABLE `glpi_items_devicesimcards` ( `lines_id` int unsigned NOT NULL DEFAULT '0', `users_id` int unsigned NOT NULL DEFAULT '0', `users_id_tech` int unsigned NOT NULL DEFAULT '0', - `groups_id` int unsigned NOT NULL DEFAULT '0', - `groups_id_tech` int unsigned NOT NULL DEFAULT '0', `pin` varchar(255) NOT NULL DEFAULT '', `pin2` varchar(255) NOT NULL DEFAULT '', `puk` varchar(255) NOT NULL DEFAULT '', @@ -2493,9 +2475,7 @@ CREATE TABLE `glpi_items_devicesimcards` ( KEY `locations_id` (`locations_id`), KEY `lines_id` (`lines_id`), KEY `users_id` (`users_id`), - KEY `users_id_tech` (`users_id_tech`), - KEY `groups_id` (`groups_id`), - KEY `groups_id_tech` (`groups_id_tech`) + KEY `users_id_tech` (`users_id_tech`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; @@ -2692,8 +2672,8 @@ CREATE TABLE `glpi_domains` ( `domaintypes_id` int unsigned NOT NULL DEFAULT '0', `date_expiration` timestamp NULL DEFAULT NULL, `date_domaincreation` timestamp NULL DEFAULT NULL, + `users_id` int unsigned NOT NULL DEFAULT '0', `users_id_tech` int unsigned NOT NULL DEFAULT '0', - `groups_id_tech` int unsigned NOT NULL DEFAULT '0', `is_deleted` tinyint NOT NULL DEFAULT '0', `comment` text, `is_template` tinyint NOT NULL DEFAULT '0', @@ -2706,8 +2686,8 @@ CREATE TABLE `glpi_domains` ( KEY `entities_id` (`entities_id`), KEY `is_recursive` (`is_recursive`), KEY `domaintypes_id` (`domaintypes_id`), + KEY `users_id` (`users_id`), KEY `users_id_tech` (`users_id_tech`), - KEY `groups_id_tech` (`groups_id_tech`), KEY `date_mod` (`date_mod`), KEY `is_deleted` (`is_deleted`), KEY `is_template` (`is_template`), @@ -4110,8 +4090,6 @@ CREATE TABLE `glpi_lines` ( `caller_name` varchar(255) NOT NULL DEFAULT '', `users_id` int unsigned NOT NULL DEFAULT '0', `users_id_tech` int unsigned NOT NULL DEFAULT '0', - `groups_id` int unsigned NOT NULL DEFAULT '0', - `groups_id_tech` int unsigned NOT NULL DEFAULT '0', `lineoperators_id` int unsigned NOT NULL DEFAULT '0', `locations_id` int unsigned NOT NULL DEFAULT '0', `states_id` int unsigned NOT NULL DEFAULT '0', @@ -4127,8 +4105,6 @@ CREATE TABLE `glpi_lines` ( KEY `users_id` (`users_id`), KEY `users_id_tech` (`users_id_tech`), KEY `lineoperators_id` (`lineoperators_id`), - KEY `groups_id` (`groups_id`), - KEY `groups_id_tech` (`groups_id_tech`), KEY `linetypes_id` (`linetypes_id`), KEY `locations_id` (`locations_id`), KEY `states_id` (`states_id`), @@ -4355,7 +4331,6 @@ CREATE TABLE `glpi_monitors` ( `contact` varchar(255) DEFAULT NULL, `contact_num` varchar(255) DEFAULT NULL, `users_id_tech` int unsigned NOT NULL DEFAULT '0', - `groups_id_tech` int unsigned NOT NULL DEFAULT '0', `comment` text, `serial` varchar(255) DEFAULT NULL, `otherserial` varchar(255) DEFAULT NULL, @@ -4377,7 +4352,6 @@ CREATE TABLE `glpi_monitors` ( `is_template` tinyint NOT NULL DEFAULT '0', `template_name` varchar(255) DEFAULT NULL, `users_id` int unsigned NOT NULL DEFAULT '0', - `groups_id` int unsigned NOT NULL DEFAULT '0', `states_id` int unsigned NOT NULL DEFAULT '0', `ticket_tco` decimal(20,4) DEFAULT '0.0000', `is_dynamic` tinyint NOT NULL DEFAULT '0', @@ -4391,7 +4365,6 @@ CREATE TABLE `glpi_monitors` ( KEY `is_global` (`is_global`), KEY `entities_id` (`entities_id`), KEY `manufacturers_id` (`manufacturers_id`), - KEY `groups_id` (`groups_id`), KEY `users_id` (`users_id`), KEY `locations_id` (`locations_id`), KEY `monitormodels_id` (`monitormodels_id`), @@ -4399,7 +4372,6 @@ CREATE TABLE `glpi_monitors` ( KEY `users_id_tech` (`users_id_tech`), KEY `monitortypes_id` (`monitortypes_id`), KEY `is_deleted` (`is_deleted`), - KEY `groups_id_tech` (`groups_id_tech`), KEY `is_dynamic` (`is_dynamic`), KEY `autoupdatesystems_id` (`autoupdatesystems_id`), KEY `serial` (`serial`), @@ -4477,6 +4449,7 @@ CREATE TABLE `glpi_cables` ( `color` varchar(255) DEFAULT NULL, `otherserial` varchar(255) DEFAULT NULL, `states_id` int unsigned NOT NULL DEFAULT '0', + `users_id` int unsigned NOT NULL DEFAULT '0', `users_id_tech` int unsigned NOT NULL DEFAULT '0', `cabletypes_id` int unsigned NOT NULL DEFAULT '0', `comment` text, @@ -4498,6 +4471,7 @@ CREATE TABLE `glpi_cables` ( KEY `complete` (`entities_id`,`name`), KEY `is_recursive` (`is_recursive`), KEY `is_template` (`is_template`), + KEY `users_id` (`users_id`), KEY `users_id_tech` (`users_id_tech`), KEY `cabletypes_id` (`cabletypes_id`), KEY `date_mod` (`date_mod`), @@ -4612,7 +4586,6 @@ CREATE TABLE `glpi_networkequipments` ( `contact` varchar(255) DEFAULT NULL, `contact_num` varchar(255) DEFAULT NULL, `users_id_tech` int unsigned NOT NULL DEFAULT '0', - `groups_id_tech` int unsigned NOT NULL DEFAULT '0', `date_mod` timestamp NULL DEFAULT NULL, `comment` text, `locations_id` int unsigned NOT NULL DEFAULT '0', @@ -4624,7 +4597,6 @@ CREATE TABLE `glpi_networkequipments` ( `is_template` tinyint NOT NULL DEFAULT '0', `template_name` varchar(255) DEFAULT NULL, `users_id` int unsigned NOT NULL DEFAULT '0', - `groups_id` int unsigned NOT NULL DEFAULT '0', `states_id` int unsigned NOT NULL DEFAULT '0', `ticket_tco` decimal(20,4) DEFAULT '0.0000', `is_dynamic` tinyint NOT NULL DEFAULT '0', @@ -4642,7 +4614,6 @@ CREATE TABLE `glpi_networkequipments` ( KEY `entities_id` (`entities_id`), KEY `is_recursive` (`is_recursive`), KEY `manufacturers_id` (`manufacturers_id`), - KEY `groups_id` (`groups_id`), KEY `users_id` (`users_id`), KEY `locations_id` (`locations_id`), KEY `networkequipmentmodels_id` (`networkequipmentmodels_id`), @@ -4652,7 +4623,6 @@ CREATE TABLE `glpi_networkequipments` ( KEY `networkequipmenttypes_id` (`networkequipmenttypes_id`), KEY `is_deleted` (`is_deleted`), KEY `date_mod` (`date_mod`), - KEY `groups_id_tech` (`groups_id_tech`), KEY `is_dynamic` (`is_dynamic`), KEY `serial` (`serial`), KEY `otherserial` (`otherserial`), @@ -5183,8 +5153,8 @@ CREATE TABLE `glpi_passivedcequipments` ( `otherserial` varchar(255) DEFAULT NULL, `passivedcequipmentmodels_id` int unsigned DEFAULT NULL, `passivedcequipmenttypes_id` int unsigned NOT NULL DEFAULT '0', + `users_id` int unsigned NOT NULL DEFAULT '0', `users_id_tech` int unsigned NOT NULL DEFAULT '0', - `groups_id_tech` int unsigned NOT NULL DEFAULT '0', `is_template` tinyint NOT NULL DEFAULT '0', `template_name` varchar(255) DEFAULT NULL, `is_deleted` tinyint NOT NULL DEFAULT '0', @@ -5200,8 +5170,8 @@ CREATE TABLE `glpi_passivedcequipments` ( KEY `locations_id` (`locations_id`), KEY `passivedcequipmentmodels_id` (`passivedcequipmentmodels_id`), KEY `passivedcequipmenttypes_id` (`passivedcequipmenttypes_id`), + KEY `users_id` (`users_id`), KEY `users_id_tech` (`users_id_tech`), - KEY `group_id_tech` (`groups_id_tech`), KEY `is_template` (`is_template`), KEY `is_deleted` (`is_deleted`), KEY `states_id` (`states_id`), @@ -5292,7 +5262,6 @@ CREATE TABLE `glpi_peripherals` ( `contact` varchar(255) DEFAULT NULL, `contact_num` varchar(255) DEFAULT NULL, `users_id_tech` int unsigned NOT NULL DEFAULT '0', - `groups_id_tech` int unsigned NOT NULL DEFAULT '0', `comment` text, `serial` varchar(255) DEFAULT NULL, `otherserial` varchar(255) DEFAULT NULL, @@ -5306,7 +5275,6 @@ CREATE TABLE `glpi_peripherals` ( `is_template` tinyint NOT NULL DEFAULT '0', `template_name` varchar(255) DEFAULT NULL, `users_id` int unsigned NOT NULL DEFAULT '0', - `groups_id` int unsigned NOT NULL DEFAULT '0', `states_id` int unsigned NOT NULL DEFAULT '0', `ticket_tco` decimal(20,4) DEFAULT '0.0000', `is_dynamic` tinyint NOT NULL DEFAULT '0', @@ -5320,7 +5288,6 @@ CREATE TABLE `glpi_peripherals` ( KEY `is_global` (`is_global`), KEY `entities_id` (`entities_id`), KEY `manufacturers_id` (`manufacturers_id`), - KEY `groups_id` (`groups_id`), KEY `users_id` (`users_id`), KEY `locations_id` (`locations_id`), KEY `peripheralmodels_id` (`peripheralmodels_id`), @@ -5329,7 +5296,6 @@ CREATE TABLE `glpi_peripherals` ( KEY `peripheraltypes_id` (`peripheraltypes_id`), KEY `is_deleted` (`is_deleted`), KEY `date_mod` (`date_mod`), - KEY `groups_id_tech` (`groups_id_tech`), KEY `is_dynamic` (`is_dynamic`), KEY `autoupdatesystems_id` (`autoupdatesystems_id`), KEY `serial` (`serial`), @@ -5404,7 +5370,6 @@ CREATE TABLE `glpi_phones` ( `contact` varchar(255) DEFAULT NULL, `contact_num` varchar(255) DEFAULT NULL, `users_id_tech` int unsigned NOT NULL DEFAULT '0', - `groups_id_tech` int unsigned NOT NULL DEFAULT '0', `comment` text, `serial` varchar(255) DEFAULT NULL, `otherserial` varchar(255) DEFAULT NULL, @@ -5422,7 +5387,6 @@ CREATE TABLE `glpi_phones` ( `is_template` tinyint NOT NULL DEFAULT '0', `template_name` varchar(255) DEFAULT NULL, `users_id` int unsigned NOT NULL DEFAULT '0', - `groups_id` int unsigned NOT NULL DEFAULT '0', `states_id` int unsigned NOT NULL DEFAULT '0', `ticket_tco` decimal(20,4) DEFAULT '0.0000', `is_dynamic` tinyint NOT NULL DEFAULT '0', @@ -5437,7 +5401,6 @@ CREATE TABLE `glpi_phones` ( KEY `is_global` (`is_global`), KEY `entities_id` (`entities_id`), KEY `manufacturers_id` (`manufacturers_id`), - KEY `groups_id` (`groups_id`), KEY `users_id` (`users_id`), KEY `locations_id` (`locations_id`), KEY `phonemodels_id` (`phonemodels_id`), @@ -5447,7 +5410,6 @@ CREATE TABLE `glpi_phones` ( KEY `phonetypes_id` (`phonetypes_id`), KEY `is_deleted` (`is_deleted`), KEY `date_mod` (`date_mod`), - KEY `groups_id_tech` (`groups_id_tech`), KEY `is_dynamic` (`is_dynamic`), KEY `autoupdatesystems_id` (`autoupdatesystems_id`), KEY `serial` (`serial`), @@ -5544,7 +5506,6 @@ CREATE TABLE `glpi_printers` ( `contact` varchar(255) DEFAULT NULL, `contact_num` varchar(255) DEFAULT NULL, `users_id_tech` int unsigned NOT NULL DEFAULT '0', - `groups_id_tech` int unsigned NOT NULL DEFAULT '0', `serial` varchar(255) DEFAULT NULL, `otherserial` varchar(255) DEFAULT NULL, `have_serial` tinyint NOT NULL DEFAULT '0', @@ -5566,7 +5527,6 @@ CREATE TABLE `glpi_printers` ( `init_pages_counter` int NOT NULL DEFAULT '0', `last_pages_counter` int NOT NULL DEFAULT '0', `users_id` int unsigned NOT NULL DEFAULT '0', - `groups_id` int unsigned NOT NULL DEFAULT '0', `states_id` int unsigned NOT NULL DEFAULT '0', `ticket_tco` decimal(20,4) DEFAULT '0.0000', `is_dynamic` tinyint NOT NULL DEFAULT '0', @@ -5583,7 +5543,6 @@ CREATE TABLE `glpi_printers` ( KEY `entities_id` (`entities_id`), KEY `is_recursive` (`is_recursive`), KEY `manufacturers_id` (`manufacturers_id`), - KEY `groups_id` (`groups_id`), KEY `users_id` (`users_id`), KEY `locations_id` (`locations_id`), KEY `printermodels_id` (`printermodels_id`), @@ -5593,7 +5552,6 @@ CREATE TABLE `glpi_printers` ( KEY `printertypes_id` (`printertypes_id`), KEY `is_deleted` (`is_deleted`), KEY `date_mod` (`date_mod`), - KEY `groups_id_tech` (`groups_id_tech`), KEY `last_pages_counter` (`last_pages_counter`), KEY `is_dynamic` (`is_dynamic`), KEY `serial` (`serial`), @@ -6751,8 +6709,6 @@ CREATE TABLE `glpi_softwarelicenses` ( `locations_id` int unsigned NOT NULL DEFAULT '0', `users_id_tech` int unsigned NOT NULL DEFAULT '0', `users_id` int unsigned NOT NULL DEFAULT '0', - `groups_id_tech` int unsigned NOT NULL DEFAULT '0', - `groups_id` int unsigned NOT NULL DEFAULT '0', `is_helpdesk_visible` tinyint NOT NULL DEFAULT '0', `is_template` tinyint NOT NULL DEFAULT '0', `template_name` varchar(255) DEFAULT NULL, @@ -6778,8 +6734,6 @@ CREATE TABLE `glpi_softwarelicenses` ( KEY `locations_id` (`locations_id`), KEY `users_id_tech` (`users_id_tech`), KEY `users_id` (`users_id`), - KEY `groups_id_tech` (`groups_id_tech`), - KEY `groups_id` (`groups_id`), KEY `is_helpdesk_visible` (`is_helpdesk_visible`), KEY `is_deleted` (`is_deleted`), KEY `date_creation` (`date_creation`), @@ -6829,7 +6783,6 @@ CREATE TABLE `glpi_softwares` ( `comment` text, `locations_id` int unsigned NOT NULL DEFAULT '0', `users_id_tech` int unsigned NOT NULL DEFAULT '0', - `groups_id_tech` int unsigned NOT NULL DEFAULT '0', `is_update` tinyint NOT NULL DEFAULT '0', `softwares_id` int unsigned NOT NULL DEFAULT '0', `manufacturers_id` int unsigned NOT NULL DEFAULT '0', @@ -6838,7 +6791,6 @@ CREATE TABLE `glpi_softwares` ( `template_name` varchar(255) DEFAULT NULL, `date_mod` timestamp NULL DEFAULT NULL, `users_id` int unsigned NOT NULL DEFAULT '0', - `groups_id` int unsigned NOT NULL DEFAULT '0', `ticket_tco` decimal(20,4) DEFAULT '0.0000', `is_helpdesk_visible` tinyint NOT NULL DEFAULT '1', `softwarecategories_id` int unsigned NOT NULL DEFAULT '0', @@ -6854,14 +6806,12 @@ CREATE TABLE `glpi_softwares` ( KEY `entities_id` (`entities_id`), KEY `is_recursive` (`is_recursive`), KEY `manufacturers_id` (`manufacturers_id`), - KEY `groups_id` (`groups_id`), KEY `users_id` (`users_id`), KEY `locations_id` (`locations_id`), KEY `users_id_tech` (`users_id_tech`), KEY `softwares_id` (`softwares_id`), KEY `is_deleted` (`is_deleted`), KEY `is_helpdesk_visible` (`is_helpdesk_visible`), - KEY `groups_id_tech` (`groups_id_tech`), KEY `date_creation` (`date_creation`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; @@ -8302,9 +8252,7 @@ CREATE TABLE `glpi_racks` ( `racktypes_id` int unsigned NOT NULL DEFAULT '0', `states_id` int unsigned NOT NULL DEFAULT '0', `users_id` int unsigned NOT NULL DEFAULT '0', - `groups_id` int unsigned NOT NULL DEFAULT '0', `users_id_tech` int unsigned NOT NULL DEFAULT '0', - `groups_id_tech` int unsigned NOT NULL DEFAULT '0', `width` int DEFAULT NULL, `height` int DEFAULT NULL, `depth` int DEFAULT NULL, @@ -8331,9 +8279,7 @@ CREATE TABLE `glpi_racks` ( KEY `racktypes_id` (`racktypes_id`), KEY `states_id` (`states_id`), KEY `users_id` (`users_id`), - KEY `groups_id` (`groups_id`), KEY `users_id_tech` (`users_id_tech`), - KEY `group_id_tech` (`groups_id_tech`), KEY `is_template` (`is_template`), KEY `is_deleted` (`is_deleted`), KEY `dcrooms_id` (`dcrooms_id`), @@ -8391,8 +8337,8 @@ CREATE TABLE `glpi_enclosures` ( `serial` varchar(255) DEFAULT NULL, `otherserial` varchar(255) DEFAULT NULL, `enclosuremodels_id` int unsigned DEFAULT NULL, + `users_id` int unsigned NOT NULL DEFAULT '0', `users_id_tech` int unsigned NOT NULL DEFAULT '0', - `groups_id_tech` int unsigned NOT NULL DEFAULT '0', `is_template` tinyint NOT NULL DEFAULT '0', `template_name` varchar(255) DEFAULT NULL, `is_deleted` tinyint NOT NULL DEFAULT '0', @@ -8409,8 +8355,8 @@ CREATE TABLE `glpi_enclosures` ( KEY `is_recursive` (`is_recursive`), KEY `locations_id` (`locations_id`), KEY `enclosuremodels_id` (`enclosuremodels_id`), + KEY `users_id` (`users_id`), KEY `users_id_tech` (`users_id_tech`), - KEY `group_id_tech` (`groups_id_tech`), KEY `is_template` (`is_template`), KEY `is_deleted` (`is_deleted`), KEY `states_id` (`states_id`), @@ -8485,8 +8431,8 @@ CREATE TABLE `glpi_pdus` ( `serial` varchar(255) DEFAULT NULL, `otherserial` varchar(255) DEFAULT NULL, `pdumodels_id` int unsigned DEFAULT NULL, + `users_id` int unsigned NOT NULL DEFAULT '0', `users_id_tech` int unsigned NOT NULL DEFAULT '0', - `groups_id_tech` int unsigned NOT NULL DEFAULT '0', `is_template` tinyint NOT NULL DEFAULT '0', `template_name` varchar(255) DEFAULT NULL, `is_deleted` tinyint NOT NULL DEFAULT '0', @@ -8502,8 +8448,8 @@ CREATE TABLE `glpi_pdus` ( KEY `is_recursive` (`is_recursive`), KEY `locations_id` (`locations_id`), KEY `pdumodels_id` (`pdumodels_id`), + KEY `users_id` (`users_id`), KEY `users_id_tech` (`users_id_tech`), - KEY `group_id_tech` (`groups_id_tech`), KEY `is_template` (`is_template`), KEY `is_deleted` (`is_deleted`), KEY `states_id` (`states_id`), @@ -8637,8 +8583,8 @@ CREATE TABLE `glpi_clusters` ( `name` varchar(255) DEFAULT NULL, `uuid` varchar(255) DEFAULT NULL, `version` varchar(255) DEFAULT NULL, + `users_id` int unsigned NOT NULL DEFAULT '0', `users_id_tech` int unsigned NOT NULL DEFAULT '0', - `groups_id_tech` int unsigned NOT NULL DEFAULT '0', `is_deleted` tinyint NOT NULL DEFAULT '0', `states_id` int unsigned NOT NULL DEFAULT '0' COMMENT 'RELATION to states (id)', `comment` text, @@ -8648,8 +8594,8 @@ CREATE TABLE `glpi_clusters` ( `date_creation` timestamp NULL DEFAULT NULL, PRIMARY KEY (`id`), KEY `name` (`name`), + KEY `users_id` (`users_id`), KEY `users_id_tech` (`users_id_tech`), - KEY `group_id_tech` (`groups_id_tech`), KEY `is_deleted` (`is_deleted`), KEY `states_id` (`states_id`), KEY `clustertypes_id` (`clustertypes_id`), @@ -8856,8 +8802,8 @@ CREATE TABLE `glpi_domainrecords` ( `domains_id` int unsigned NOT NULL DEFAULT '0', `domainrecordtypes_id` int unsigned NOT NULL DEFAULT '0', `ttl` int NOT NULL, + `users_id` int unsigned NOT NULL DEFAULT '0', `users_id_tech` int unsigned NOT NULL DEFAULT '0', - `groups_id_tech` int unsigned NOT NULL DEFAULT '0', `is_deleted` tinyint NOT NULL DEFAULT '0', `comment` text, `date_mod` timestamp NULL DEFAULT NULL, @@ -8868,8 +8814,8 @@ CREATE TABLE `glpi_domainrecords` ( KEY `is_recursive` (`is_recursive`), KEY `domains_id` (`domains_id`), KEY `domainrecordtypes_id` (`domainrecordtypes_id`), + KEY `users_id` (`users_id`), KEY `users_id_tech` (`users_id_tech`), - KEY `groups_id_tech` (`groups_id_tech`), KEY `date_mod` (`date_mod`), KEY `is_deleted` (`is_deleted`), KEY `date_creation` (`date_creation`) @@ -8889,8 +8835,6 @@ CREATE TABLE `glpi_appliances` ( `applianceenvironments_id` int unsigned NOT NULL DEFAULT '0', `users_id` int unsigned NOT NULL DEFAULT '0', `users_id_tech` int unsigned NOT NULL DEFAULT '0', - `groups_id` int unsigned NOT NULL DEFAULT '0', - `groups_id_tech` int unsigned NOT NULL DEFAULT '0', `date_mod` timestamp NULL DEFAULT NULL, `date_creation` timestamp NULL DEFAULT NULL, `states_id` int unsigned NOT NULL DEFAULT '0', @@ -8913,8 +8857,6 @@ CREATE TABLE `glpi_appliances` ( KEY `applianceenvironments_id` (`applianceenvironments_id`), KEY `users_id` (`users_id`), KEY `users_id_tech` (`users_id_tech`), - KEY `groups_id` (`groups_id`), - KEY `groups_id_tech` (`groups_id_tech`), KEY `states_id` (`states_id`), KEY `serial` (`serial`), KEY `otherserial` (`otherserial`), @@ -9065,10 +9007,8 @@ CREATE TABLE `glpi_unmanageds` ( `manufacturers_id` int unsigned NOT NULL DEFAULT '0', `is_deleted` tinyint NOT NULL DEFAULT '0', `users_id` int unsigned NOT NULL DEFAULT '0', - `groups_id` int unsigned NOT NULL DEFAULT '0', `states_id` int unsigned NOT NULL DEFAULT '0', `users_id_tech` int unsigned NOT NULL DEFAULT '0', - `groups_id_tech` int unsigned NOT NULL DEFAULT '0', `is_dynamic` tinyint NOT NULL DEFAULT '0', `date_creation` timestamp NULL DEFAULT NULL, `autoupdatesystems_id` int unsigned NOT NULL DEFAULT '0', @@ -9085,12 +9025,10 @@ CREATE TABLE `glpi_unmanageds` ( KEY `entities_id` (`entities_id`), KEY `is_recursive` (`is_recursive`), KEY `manufacturers_id` (`manufacturers_id`), - KEY `groups_id` (`groups_id`), KEY `users_id` (`users_id`), KEY `locations_id` (`locations_id`), KEY `networks_id` (`networks_id`), KEY `states_id` (`states_id`), - KEY `groups_id_tech` (`groups_id_tech`), KEY `is_deleted` (`is_deleted`), KEY `date_mod` (`date_mod`), KEY `is_dynamic` (`is_dynamic`), @@ -9380,8 +9318,6 @@ CREATE TABLE `glpi_databaseinstances` ( `manufacturers_id` int unsigned NOT NULL DEFAULT '0', `users_id` int unsigned NOT NULL DEFAULT '0', `users_id_tech` int unsigned NOT NULL DEFAULT '0', - `groups_id` int unsigned NOT NULL DEFAULT '0', - `groups_id_tech` int unsigned NOT NULL DEFAULT '0', `states_id` int unsigned NOT NULL DEFAULT '0', `itemtype` varchar(100) NOT NULL DEFAULT '', `items_id` int unsigned NOT NULL DEFAULT '0', @@ -9406,8 +9342,6 @@ CREATE TABLE `glpi_databaseinstances` ( KEY `manufacturers_id` (`manufacturers_id`), KEY `users_id` (`users_id`), KEY `users_id_tech` (`users_id_tech`), - KEY `groups_id` (`groups_id`), - KEY `groups_id_tech` (`groups_id_tech`), KEY `states_id` (`states_id`), KEY `item` (`itemtype`,`items_id`), KEY `is_active` (`is_active`), @@ -9881,9 +9815,7 @@ CREATE TABLE `glpi_assets_assets` ( `contact` varchar(255) DEFAULT NULL, `contact_num` varchar(255) DEFAULT NULL, `users_id` int unsigned NOT NULL DEFAULT '0', - `groups_id` int unsigned NOT NULL DEFAULT '0', `users_id_tech` int unsigned NOT NULL DEFAULT '0', - `groups_id_tech` int unsigned NOT NULL DEFAULT '0', `locations_id` int unsigned NOT NULL DEFAULT '0', `manufacturers_id` int unsigned NOT NULL DEFAULT '0', `states_id` int unsigned NOT NULL DEFAULT '0', @@ -9900,9 +9832,7 @@ CREATE TABLE `glpi_assets_assets` ( KEY `assets_assettypes_id` (`assets_assettypes_id`), KEY `name` (`name`), KEY `users_id` (`users_id`), - KEY `groups_id` (`groups_id`), KEY `users_id_tech` (`users_id_tech`), - KEY `groups_id_tech` (`groups_id_tech`), KEY `locations_id` (`locations_id`), KEY `manufacturers_id` (`manufacturers_id`), KEY `states_id` (`states_id`), @@ -9955,4 +9885,17 @@ CREATE TABLE `glpi_assets_assettypes` ( KEY `date_creation` (`date_creation`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +DROP TABLE IF EXISTS `glpi_groups_items`; +CREATE TABLE `glpi_groups_items` ( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `groups_id` int unsigned NOT NULL DEFAULT '0', + `itemtype` varchar(255) NOT NULL DEFAULT '', + `items_id` int unsigned NOT NULL DEFAULT '0', + `type` tinyint NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `unicity` (`groups_id`,`itemtype`,`items_id`, `type`), + KEY `item` (`itemtype`, `items_id`), + KEY `type` (`type`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; + SET FOREIGN_KEY_CHECKS=1; diff --git a/src/Api/HL/Controller/AdministrationController.php b/src/Api/HL/Controller/AdministrationController.php index 9ca17da9377..7ba448d9f79 100644 --- a/src/Api/HL/Controller/AdministrationController.php +++ b/src/Api/HL/Controller/AdministrationController.php @@ -704,8 +704,7 @@ private function getUsedOrManagedItems(int $users_id, bool $is_managed, array $r /** @var array $CFG_GLPI */ global $CFG_GLPI; // Create a union schema with all relevant item types - $types = $is_managed ? $CFG_GLPI['linkuser_tech_types'] : $CFG_GLPI['linkuser_types']; - $schema = Doc\Schema::getUnionSchemaForItemtypes($types); + $schema = Doc\Schema::getUnionSchemaForItemtypes($CFG_GLPI['assignable_types']); $rsql_filter = $request_params['filter'] ?? ''; if (!empty($rsql_filter)) { $rsql_filter = "($rsql_filter);"; diff --git a/src/Api/HL/Controller/AssetController.php b/src/Api/HL/Controller/AssetController.php index 0f7dd1b613a..33f3cad7a3c 100644 --- a/src/Api/HL/Controller/AssetController.php +++ b/src/Api/HL/Controller/AssetController.php @@ -48,10 +48,12 @@ use Glpi\Socket; use Glpi\SocketModel; use Group; +use Group_Item; use GuzzleHttp\Psr7\Utils; use Location; use Manufacturer; use Network; +use Software; use State; use User; @@ -387,33 +389,75 @@ class: $model_class, ); } - if (in_array($asset_type, $CFG_GLPI['linkuser_tech_types'], true)) { - $schemas[$schema_name]['properties']['user_tech'] = self::getDropdownTypeSchema( - class: User::class, - field: 'users_id_tech', - full_schema: 'User' - ); - } - if (in_array($asset_type, $CFG_GLPI['linkgroup_tech_types'], true)) { - $schemas[$schema_name]['properties']['group_tech'] = self::getDropdownTypeSchema( - class: Group::class, - field: 'groups_id_tech', - full_schema: 'Group' - ); - } - if (in_array($asset_type, $CFG_GLPI['linkuser_types'], true)) { + if (in_array($asset_type, $CFG_GLPI['assignable_types'], true)) { $schemas[$schema_name]['properties']['user'] = self::getDropdownTypeSchema( class: User::class, field: 'users_id', full_schema: 'User' ); - } - if (in_array($asset_type, $CFG_GLPI['linkgroup_types'], true)) { - $schemas[$schema_name]['properties']['group'] = self::getDropdownTypeSchema( - class: Group::class, - field: 'groups_id', - full_schema: 'Group' + $schemas[$schema_name]['properties']['user_tech'] = self::getDropdownTypeSchema( + class: User::class, + field: 'users_id_tech', + full_schema: 'User' ); + $schemas[$schema_name]['properties']['group'] = [ + 'type' => Doc\Schema::TYPE_ARRAY, + 'items' => [ + 'type' => Doc\Schema::TYPE_OBJECT, + 'x-full-schema' => 'Group', + 'x-join' => [ + 'table' => 'glpi_groups', // The table with the desired data + 'fkey' => 'groups_id', + 'field' => 'id', + 'ref-join' => [ + 'table' => 'glpi_groups_items', + 'fkey' => 'id', + 'field' => 'items_id', + 'condition' => [ + 'itemtype' => $asset_type, + 'type' => Group_Item::GROUP_TYPE_NORMAL, + ] + ] + ], + 'properties' => [ + 'id' => [ + 'type' => Doc\Schema::TYPE_INTEGER, + 'format' => Doc\Schema::FORMAT_INTEGER_INT64, + 'description' => 'ID', + ], + 'name' => ['type' => Doc\Schema::TYPE_STRING], + ] + ] + ]; + $schemas[$schema_name]['properties']['group_tech'] = [ + 'type' => Doc\Schema::TYPE_ARRAY, + 'items' => [ + 'type' => Doc\Schema::TYPE_OBJECT, + 'x-full-schema' => 'Group', + 'x-join' => [ + 'table' => 'glpi_groups', // The table with the desired data + 'fkey' => 'groups_id', + 'field' => 'id', + 'ref-join' => [ + 'table' => 'glpi_groups_items', + 'fkey' => 'id', + 'field' => 'items_id', + 'condition' => [ + 'itemtype' => $asset_type, + 'type' => Group_Item::GROUP_TYPE_TECH, + ] + ] + ], + 'properties' => [ + 'id' => [ + 'type' => Doc\Schema::TYPE_INTEGER, + 'format' => Doc\Schema::FORMAT_INTEGER_INT64, + 'description' => 'ID', + ], + 'name' => ['type' => Doc\Schema::TYPE_STRING], + ] + ] + ]; } if ($asset->isField('contact')) { @@ -632,7 +676,7 @@ class: Group::class, ]; $schemas['Software'] = [ - 'x-itemtype' => \Software::class, + 'x-itemtype' => Software::class, 'type' => Doc\Schema::TYPE_OBJECT, 'properties' => [ 'id' => [ @@ -650,9 +694,65 @@ class: Group::class, 'parent' => self::getDropdownTypeSchema(class: \Software::class, full_schema: 'Software'), 'is_helpdesk_visible' => ['type' => Doc\Schema::TYPE_BOOLEAN], 'user' => self::getDropdownTypeSchema(class: User::class, full_schema: 'User'), - 'group' => self::getDropdownTypeSchema(class: Group::class, full_schema: 'Group'), + 'group' => [ + 'type' => Doc\Schema::TYPE_ARRAY, + 'items' => [ + 'type' => Doc\Schema::TYPE_OBJECT, + 'x-full-schema' => 'Group', + 'x-join' => [ + 'table' => 'glpi_groups', // The table with the desired data + 'fkey' => 'groups_id', + 'field' => 'id', + 'ref-join' => [ + 'table' => 'glpi_groups_items', + 'fkey' => 'id', + 'field' => 'items_id', + 'condition' => [ + 'itemtype' => Software::class, + 'type' => Group_Item::GROUP_TYPE_NORMAL, + ] + ] + ], + 'properties' => [ + 'id' => [ + 'type' => Doc\Schema::TYPE_INTEGER, + 'format' => Doc\Schema::FORMAT_INTEGER_INT64, + 'description' => 'ID', + ], + 'name' => ['type' => Doc\Schema::TYPE_STRING], + ] + ] + ], 'user_tech' => self::getDropdownTypeSchema(class: User::class, field: 'users_id_tech', full_schema: 'User'), - 'group_tech' => self::getDropdownTypeSchema(class: Group::class, field: 'groups_id_tech', full_schema: 'Group'), + 'group_tech' => [ + 'type' => Doc\Schema::TYPE_ARRAY, + 'items' => [ + 'type' => Doc\Schema::TYPE_OBJECT, + 'x-full-schema' => 'Group', + 'x-join' => [ + 'table' => 'glpi_groups', // The table with the desired data + 'fkey' => 'groups_id', + 'field' => 'id', + 'ref-join' => [ + 'table' => 'glpi_groups_items', + 'fkey' => 'id', + 'field' => 'items_id', + 'condition' => [ + 'itemtype' => Software::class, + 'type' => Group_Item::GROUP_TYPE_TECH, + ] + ] + ], + 'properties' => [ + 'id' => [ + 'type' => Doc\Schema::TYPE_INTEGER, + 'format' => Doc\Schema::FORMAT_INTEGER_INT64, + 'description' => 'ID', + ], + 'name' => ['type' => Doc\Schema::TYPE_STRING], + ] + ] + ], 'is_deleted' => ['type' => Doc\Schema::TYPE_BOOLEAN], 'is_update' => ['type' => Doc\Schema::TYPE_BOOLEAN], 'is_valid' => ['type' => Doc\Schema::TYPE_BOOLEAN], @@ -703,8 +803,66 @@ class: Group::class, 'manufacturer' => self::getDropdownTypeSchema(class: Manufacturer::class, full_schema: 'Manufacturer'), 'type' => self::getDropdownTypeSchema(class: \RackType::class, full_schema: 'RackType'), 'state' => self::getDropdownTypeSchema(class: \State::class, full_schema: 'State'), + 'user' => self::getDropdownTypeSchema(class: User::class, full_schema: 'User'), + 'group' => [ + 'type' => Doc\Schema::TYPE_ARRAY, + 'items' => [ + 'type' => Doc\Schema::TYPE_OBJECT, + 'x-full-schema' => 'Group', + 'x-join' => [ + 'table' => 'glpi_groups', // The table with the desired data + 'fkey' => 'groups_id', + 'field' => 'id', + 'ref-join' => [ + 'table' => 'glpi_groups_items', + 'fkey' => 'id', + 'field' => 'items_id', + 'condition' => [ + 'itemtype' => \Rack::class, + 'type' => Group_Item::GROUP_TYPE_NORMAL, + ] + ] + ], + 'properties' => [ + 'id' => [ + 'type' => Doc\Schema::TYPE_INTEGER, + 'format' => Doc\Schema::FORMAT_INTEGER_INT64, + 'description' => 'ID', + ], + 'name' => ['type' => Doc\Schema::TYPE_STRING], + ] + ] + ], 'user_tech' => self::getDropdownTypeSchema(class: User::class, field: 'users_id_tech', full_schema: 'User'), - 'group_tech' => self::getDropdownTypeSchema(class: Group::class, field: 'groups_id_tech', full_schema: 'Group'), + 'group_tech' => [ + 'type' => Doc\Schema::TYPE_ARRAY, + 'items' => [ + 'type' => Doc\Schema::TYPE_OBJECT, + 'x-full-schema' => 'Group', + 'x-join' => [ + 'table' => 'glpi_groups', // The table with the desired data + 'fkey' => 'groups_id', + 'field' => 'id', + 'ref-join' => [ + 'table' => 'glpi_groups_items', + 'fkey' => 'id', + 'field' => 'items_id', + 'condition' => [ + 'itemtype' => \Rack::class, + 'type' => Group_Item::GROUP_TYPE_TECH, + ] + ] + ], + 'properties' => [ + 'id' => [ + 'type' => Doc\Schema::TYPE_INTEGER, + 'format' => Doc\Schema::FORMAT_INTEGER_INT64, + 'description' => 'ID', + ], + 'name' => ['type' => Doc\Schema::TYPE_STRING], + ] + ] + ], 'width' => ['type' => Doc\Schema::TYPE_INTEGER, 'format' => Doc\Schema::FORMAT_INTEGER_INT32], 'height' => ['type' => Doc\Schema::TYPE_INTEGER, 'format' => Doc\Schema::FORMAT_INTEGER_INT32], 'depth' => ['type' => Doc\Schema::TYPE_INTEGER, 'format' => Doc\Schema::FORMAT_INTEGER_INT32], @@ -745,8 +903,66 @@ class: Group::class, 'model' => self::getDropdownTypeSchema(\EnclosureModel::class), 'manufacturer' => self::getDropdownTypeSchema(class: Manufacturer::class, full_schema: 'Manufacturer'), 'state' => self::getDropdownTypeSchema(class: \State::class, full_schema: 'State'), + 'user' => self::getDropdownTypeSchema(class: User::class, full_schema: 'User'), + 'group' => [ + 'type' => Doc\Schema::TYPE_ARRAY, + 'items' => [ + 'type' => Doc\Schema::TYPE_OBJECT, + 'x-full-schema' => 'Group', + 'x-join' => [ + 'table' => 'glpi_groups', // The table with the desired data + 'fkey' => 'groups_id', + 'field' => 'id', + 'ref-join' => [ + 'table' => 'glpi_groups_items', + 'fkey' => 'id', + 'field' => 'items_id', + 'condition' => [ + 'itemtype' => \Enclosure::class, + 'type' => Group_Item::GROUP_TYPE_NORMAL, + ] + ] + ], + 'properties' => [ + 'id' => [ + 'type' => Doc\Schema::TYPE_INTEGER, + 'format' => Doc\Schema::FORMAT_INTEGER_INT64, + 'description' => 'ID', + ], + 'name' => ['type' => Doc\Schema::TYPE_STRING], + ] + ] + ], 'user_tech' => self::getDropdownTypeSchema(class: User::class, field: 'users_id_tech', full_schema: 'User'), - 'group_tech' => self::getDropdownTypeSchema(class: Group::class, field: 'groups_id_tech', full_schema: 'Group'), + 'group_tech' => [ + 'type' => Doc\Schema::TYPE_ARRAY, + 'items' => [ + 'type' => Doc\Schema::TYPE_OBJECT, + 'x-full-schema' => 'Group', + 'x-join' => [ + 'table' => 'glpi_groups', // The table with the desired data + 'fkey' => 'groups_id', + 'field' => 'id', + 'ref-join' => [ + 'table' => 'glpi_groups_items', + 'fkey' => 'id', + 'field' => 'items_id', + 'condition' => [ + 'itemtype' => \Enclosure::class, + 'type' => Group_Item::GROUP_TYPE_TECH, + ] + ] + ], + 'properties' => [ + 'id' => [ + 'type' => Doc\Schema::TYPE_INTEGER, + 'format' => Doc\Schema::FORMAT_INTEGER_INT64, + 'description' => 'ID', + ], + 'name' => ['type' => Doc\Schema::TYPE_STRING], + ] + ] + ], 'is_deleted' => ['type' => Doc\Schema::TYPE_BOOLEAN], 'orientation' => ['type' => Doc\Schema::TYPE_INTEGER, 'format' => Doc\Schema::FORMAT_INTEGER_INT32], 'power_supplies' => ['type' => Doc\Schema::TYPE_INTEGER, 'format' => Doc\Schema::FORMAT_INTEGER_INT32], @@ -775,8 +991,66 @@ class: Group::class, 'manufacturer' => self::getDropdownTypeSchema(class: Manufacturer::class, full_schema: 'Manufacturer'), 'type' => self::getDropdownTypeSchema(class: \PDUType::class, full_schema: 'PDUType'), 'state' => self::getDropdownTypeSchema(class: \State::class, full_schema: 'State'), + 'user' => self::getDropdownTypeSchema(class: User::class, full_schema: 'User'), + 'group' => [ + 'type' => Doc\Schema::TYPE_ARRAY, + 'items' => [ + 'type' => Doc\Schema::TYPE_OBJECT, + 'x-full-schema' => 'Group', + 'x-join' => [ + 'table' => 'glpi_groups', // The table with the desired data + 'fkey' => 'groups_id', + 'field' => 'id', + 'ref-join' => [ + 'table' => 'glpi_groups_items', + 'fkey' => 'id', + 'field' => 'items_id', + 'condition' => [ + 'itemtype' => \PDU::class, + 'type' => Group_Item::GROUP_TYPE_NORMAL, + ] + ] + ], + 'properties' => [ + 'id' => [ + 'type' => Doc\Schema::TYPE_INTEGER, + 'format' => Doc\Schema::FORMAT_INTEGER_INT64, + 'description' => 'ID', + ], + 'name' => ['type' => Doc\Schema::TYPE_STRING], + ] + ] + ], 'user_tech' => self::getDropdownTypeSchema(class: User::class, field: 'users_id_tech', full_schema: 'User'), - 'group_tech' => self::getDropdownTypeSchema(class: Group::class, field: 'groups_id_tech', full_schema: 'Group'), + 'group_tech' => [ + 'type' => Doc\Schema::TYPE_ARRAY, + 'items' => [ + 'type' => Doc\Schema::TYPE_OBJECT, + 'x-full-schema' => 'Group', + 'x-join' => [ + 'table' => 'glpi_groups', // The table with the desired data + 'fkey' => 'groups_id', + 'field' => 'id', + 'ref-join' => [ + 'table' => 'glpi_groups_items', + 'fkey' => 'id', + 'field' => 'items_id', + 'condition' => [ + 'itemtype' => \PDU::class, + 'type' => Group_Item::GROUP_TYPE_TECH, + ] + ] + ], + 'properties' => [ + 'id' => [ + 'type' => Doc\Schema::TYPE_INTEGER, + 'format' => Doc\Schema::FORMAT_INTEGER_INT64, + 'description' => 'ID', + ], + 'name' => ['type' => Doc\Schema::TYPE_STRING], + ] + ] + ], 'is_deleted' => ['type' => Doc\Schema::TYPE_BOOLEAN], 'date_creation' => ['type' => Doc\Schema::TYPE_STRING, 'format' => Doc\Schema::FORMAT_STRING_DATE_TIME], 'date_mod' => ['type' => Doc\Schema::TYPE_STRING, 'format' => Doc\Schema::FORMAT_STRING_DATE_TIME], @@ -803,8 +1077,66 @@ class: Group::class, 'manufacturer' => self::getDropdownTypeSchema(class: Manufacturer::class, full_schema: 'Manufacturer'), 'type' => self::getDropdownTypeSchema(class: \PassiveDCEquipmentType::class, full_schema: 'PassiveDCEquipmentType'), 'state' => self::getDropdownTypeSchema(class: \State::class, full_schema: 'State'), + 'user' => self::getDropdownTypeSchema(class: User::class, full_schema: 'User'), + 'group' => [ + 'type' => Doc\Schema::TYPE_ARRAY, + 'items' => [ + 'type' => Doc\Schema::TYPE_OBJECT, + 'x-full-schema' => 'Group', + 'x-join' => [ + 'table' => 'glpi_groups', // The table with the desired data + 'fkey' => 'groups_id', + 'field' => 'id', + 'ref-join' => [ + 'table' => 'glpi_groups_items', + 'fkey' => 'id', + 'field' => 'items_id', + 'condition' => [ + 'itemtype' => \PassiveDCEquipment::class, + 'type' => Group_Item::GROUP_TYPE_NORMAL, + ] + ] + ], + 'properties' => [ + 'id' => [ + 'type' => Doc\Schema::TYPE_INTEGER, + 'format' => Doc\Schema::FORMAT_INTEGER_INT64, + 'description' => 'ID', + ], + 'name' => ['type' => Doc\Schema::TYPE_STRING], + ] + ] + ], 'user_tech' => self::getDropdownTypeSchema(class: User::class, field: 'users_id_tech', full_schema: 'User'), - 'group_tech' => self::getDropdownTypeSchema(class: Group::class, field: 'groups_id_tech', full_schema: 'Group'), + 'group_tech' => [ + 'type' => Doc\Schema::TYPE_ARRAY, + 'items' => [ + 'type' => Doc\Schema::TYPE_OBJECT, + 'x-full-schema' => 'Group', + 'x-join' => [ + 'table' => 'glpi_groups', // The table with the desired data + 'fkey' => 'groups_id', + 'field' => 'id', + 'ref-join' => [ + 'table' => 'glpi_groups_items', + 'fkey' => 'id', + 'field' => 'items_id', + 'condition' => [ + 'itemtype' => \PassiveDCEquipment::class, + 'type' => Group_Item::GROUP_TYPE_TECH, + ] + ] + ], + 'properties' => [ + 'id' => [ + 'type' => Doc\Schema::TYPE_INTEGER, + 'format' => Doc\Schema::FORMAT_INTEGER_INT64, + 'description' => 'ID', + ], + 'name' => ['type' => Doc\Schema::TYPE_STRING], + ] + ] + ], 'is_deleted' => ['type' => Doc\Schema::TYPE_BOOLEAN], 'date_creation' => ['type' => Doc\Schema::TYPE_STRING, 'format' => Doc\Schema::FORMAT_STRING_DATE_TIME], 'date_mod' => ['type' => Doc\Schema::TYPE_STRING, 'format' => Doc\Schema::FORMAT_STRING_DATE_TIME], @@ -826,7 +1158,66 @@ class: Group::class, 'is_recursive' => ['type' => Doc\Schema::TYPE_BOOLEAN], 'otherserial' => ['type' => Doc\Schema::TYPE_STRING], 'state' => self::getDropdownTypeSchema(class: \State::class, full_schema: 'State'), + 'user' => self::getDropdownTypeSchema(class: User::class, full_schema: 'User'), + 'group' => [ + 'type' => Doc\Schema::TYPE_ARRAY, + 'items' => [ + 'type' => Doc\Schema::TYPE_OBJECT, + 'x-full-schema' => 'Group', + 'x-join' => [ + 'table' => 'glpi_groups', // The table with the desired data + 'fkey' => 'groups_id', + 'field' => 'id', + 'ref-join' => [ + 'table' => 'glpi_groups_items', + 'fkey' => 'id', + 'field' => 'items_id', + 'condition' => [ + 'itemtype' => \Cable::class, + 'type' => Group_Item::GROUP_TYPE_NORMAL, + ] + ] + ], + 'properties' => [ + 'id' => [ + 'type' => Doc\Schema::TYPE_INTEGER, + 'format' => Doc\Schema::FORMAT_INTEGER_INT64, + 'description' => 'ID', + ], + 'name' => ['type' => Doc\Schema::TYPE_STRING], + ] + ] + ], 'user_tech' => self::getDropdownTypeSchema(class: User::class, field: 'users_id_tech', full_schema: 'User'), + 'group_tech' => [ + 'type' => Doc\Schema::TYPE_ARRAY, + 'items' => [ + 'type' => Doc\Schema::TYPE_OBJECT, + 'x-full-schema' => 'Group', + 'x-join' => [ + 'table' => 'glpi_groups', // The table with the desired data + 'fkey' => 'groups_id', + 'field' => 'id', + 'ref-join' => [ + 'table' => 'glpi_groups_items', + 'fkey' => 'id', + 'field' => 'items_id', + 'condition' => [ + 'itemtype' => \Cable::class, + 'type' => Group_Item::GROUP_TYPE_TECH, + ] + ] + ], + 'properties' => [ + 'id' => [ + 'type' => Doc\Schema::TYPE_INTEGER, + 'format' => Doc\Schema::FORMAT_INTEGER_INT64, + 'description' => 'ID', + ], + 'name' => ['type' => Doc\Schema::TYPE_STRING], + ] + ] + ], 'is_deleted' => ['type' => Doc\Schema::TYPE_BOOLEAN], 'itemtype_endpoint_a' => ['type' => Doc\Schema::TYPE_STRING], 'items_id_endpoint_a' => ['type' => Doc\Schema::TYPE_INTEGER, 'format' => Doc\Schema::FORMAT_INTEGER_INT64], diff --git a/src/Api/HL/Controller/ManagementController.php b/src/Api/HL/Controller/ManagementController.php index 4382ebe0c04..a157e2e7007 100644 --- a/src/Api/HL/Controller/ManagementController.php +++ b/src/Api/HL/Controller/ManagementController.php @@ -55,7 +55,7 @@ use Glpi\Http\JSONResponse; use Glpi\Http\Request; use Glpi\Http\Response; -use Group; +use Group_Item; use Line; use Location; use Manufacturer; @@ -197,17 +197,70 @@ protected static function getRawKnownSchemas(): array $schemas[$m_name]['properties']['environment'] = self::getDropdownTypeSchema($env_class); } - if (in_array($m_class, $CFG_GLPI['linkuser_tech_types'], true)) { + if (in_array($m_class, $CFG_GLPI['assignable_types'], true)) { $schemas[$m_name]['properties']['user_tech'] = self::getDropdownTypeSchema(class: User::class, field: 'users_id_tech', full_schema: 'User'); - } - if (in_array($m_class, $CFG_GLPI['linkgroup_tech_types'], true)) { - $schemas[$m_name]['properties']['group_tech'] = self::getDropdownTypeSchema(class: Group::class, field: 'groups_id_tech', full_schema: 'Group'); - } - if (in_array($m_class, $CFG_GLPI['linkuser_types'], true)) { + + $schemas[$m_name]['properties']['group_tech'] = [ + 'type' => Doc\Schema::TYPE_ARRAY, + 'items' => [ + 'type' => Doc\Schema::TYPE_OBJECT, + 'x-full-schema' => 'Group', + 'x-join' => [ + 'table' => 'glpi_groups', // The table with the desired data + 'fkey' => 'groups_id', + 'field' => 'id', + 'ref-join' => [ + 'table' => 'glpi_groups_items', + 'fkey' => 'id', + 'field' => 'items_id', + 'condition' => [ + 'itemtype' => $m_class, + 'type' => Group_Item::GROUP_TYPE_TECH, + ] + ] + ], + 'properties' => [ + 'id' => [ + 'type' => Doc\Schema::TYPE_INTEGER, + 'format' => Doc\Schema::FORMAT_INTEGER_INT64, + 'description' => 'ID', + ], + 'name' => ['type' => Doc\Schema::TYPE_STRING], + ] + ] + ]; + $schemas[$m_name]['properties']['user'] = self::getDropdownTypeSchema(class: User::class, full_schema: 'User'); - } - if (in_array($m_class, $CFG_GLPI['linkgroup_types'], true)) { - $schemas[$m_name]['properties']['group'] = self::getDropdownTypeSchema(class: Group::class, full_schema: 'Group'); + + $schemas[$m_name]['properties']['group'] = [ + 'type' => Doc\Schema::TYPE_ARRAY, + 'items' => [ + 'type' => Doc\Schema::TYPE_OBJECT, + 'x-full-schema' => 'Group', + 'x-join' => [ + 'table' => 'glpi_groups', // The table with the desired data + 'fkey' => 'groups_id', + 'field' => 'id', + 'ref-join' => [ + 'table' => 'glpi_groups_items', + 'fkey' => 'id', + 'field' => 'items_id', + 'condition' => [ + 'itemtype' => $m_class, + 'type' => Group_Item::GROUP_TYPE_NORMAL, + ] + ] + ], + 'properties' => [ + 'id' => [ + 'type' => Doc\Schema::TYPE_INTEGER, + 'format' => Doc\Schema::FORMAT_INTEGER_INT64, + 'description' => 'ID', + ], + 'name' => ['type' => Doc\Schema::TYPE_STRING], + ] + ] + ]; } if ($item->isField('contact')) { diff --git a/src/Appliance.php b/src/Appliance.php index ea9a78d8ece..556c05e2cdf 100644 --- a/src/Appliance.php +++ b/src/Appliance.php @@ -35,6 +35,7 @@ use Glpi\Application\View\TemplateRenderer; use Glpi\Features\AssetImage; +use Glpi\Features\AssignableItem; /** * Appliances Class @@ -44,6 +45,10 @@ class Appliance extends CommonDBTM use Glpi\Features\Clonable; use Glpi\Features\State; use AssetImage; + use AssignableItem { + prepareInputForAdd as prepareInputForAddAssignableItem; + prepareInputForUpdate as prepareInputForUpdateAssignableItem; + } // From CommonDBTM public $dohistory = true; @@ -93,13 +98,19 @@ public function defineTabs($options = []) public function prepareInputForAdd($input) { - $input = parent::prepareInputForAdd($input); + $input = $this->prepareInputForAddAssignableItem($input); + if ($input === false) { + return false; + } return $this->managePictures($input); } public function prepareInputForUpdate($input) { - $input = parent::prepareInputForUpdate($input); + $input = $this->prepareInputForUpdateAssignableItem($input); + if ($input === false) { + return false; + } return $this->managePictures($input); } @@ -163,7 +174,18 @@ public function rawSearchOptions() 'field' => 'completename', 'name' => Group::getTypeName(1), 'condition' => ['is_itemgroup' => 1], - 'datatype' => 'dropdown' + 'joinparams' => [ + 'beforejoin' => [ + 'table' => 'glpi_groups_items', + 'joinparams' => [ + 'jointype' => 'itemtype_item', + 'condition' => ['NEWTABLE.type' => Group_Item::GROUP_TYPE_NORMAL] + ] + ] + ], + 'forcegroupby' => true, + 'massiveaction' => false, + 'datatype' => 'dropdown' ]; $tab[] = [ @@ -204,10 +226,21 @@ public function rawSearchOptions() 'id' => '49', 'table' => Group::getTable(), 'field' => 'completename', - 'linkfield' => 'groups_id_tech', + 'linkfield' => 'groups_id', 'name' => __('Group in charge'), 'condition' => ['is_assign' => 1], - 'datatype' => 'dropdown' + 'joinparams' => [ + 'beforejoin' => [ + 'table' => 'glpi_groups_items', + 'joinparams' => [ + 'jointype' => 'itemtype_item', + 'condition' => ['NEWTABLE.type' => Group_Item::GROUP_TYPE_TECH] + ] + ] + ], + 'forcegroupby' => true, + 'massiveaction' => false, + 'datatype' => 'dropdown' ]; $tab[] = [ @@ -383,11 +416,18 @@ public static function rawSearchOptionsToAdd(string $itemtype) 'datatype' => 'dropdown', 'joinparams' => [ 'beforejoin' => [ - 'table' => self::getTable(), + 'table' => 'glpi_groups_items', 'joinparams' => [ - 'beforejoin' => [ - 'table' => Appliance_Item::getTable(), - 'joinparams' => ['jointype' => 'itemtype_item'] + 'jointype' => 'itemtype_item', + 'condition' => ['NEWTABLE.type' => Group_Item::GROUP_TYPE_TECH], + 'beforejoin' => [ + 'table' => self::getTable(), + 'joinparams' => [ + 'beforejoin' => [ + 'table' => Appliance_Item::getTable(), + 'joinparams' => ['jointype' => 'itemtype_item'] + ] + ] ] ] ] diff --git a/src/Asset/Asset.php b/src/Asset/Asset.php index f729b1f839d..20803324ef5 100644 --- a/src/Asset/Asset.php +++ b/src/Asset/Asset.php @@ -37,6 +37,7 @@ use CommonDBTM; use Glpi\Application\View\TemplateRenderer; +use Group_Item; use Entity; use Group; use Location; @@ -47,7 +48,7 @@ abstract class Asset extends CommonDBTM { - use \Glpi\Features\AssignableAsset; + use \Glpi\Features\AssignableItem; use \Glpi\Features\Clonable; use \Glpi\Features\State; @@ -255,6 +256,15 @@ public function rawSearchOptions() 'field' => 'completename', 'name' => Group::getTypeName(1), 'condition' => ['is_itemgroup' => 1], + 'joinparams' => [ + 'beforejoin' => [ + 'table' => 'glpi_groups_items', + 'joinparams' => [ + 'jointype' => 'itemtype_item', + 'condition' => ['NEWTABLE.type' => Group_Item::GROUP_TYPE_NORMAL] + ] + ] + ], 'datatype' => 'dropdown' ]; @@ -302,6 +312,15 @@ public function rawSearchOptions() 'linkfield' => 'groups_id_tech', 'name' => __('Group in charge of the hardware'), 'condition' => ['is_assign' => 1], + 'joinparams' => [ + 'beforejoin' => [ + 'table' => 'glpi_groups_items', + 'joinparams' => [ + 'jointype' => 'itemtype_item', + 'condition' => ['NEWTABLE.type' => Group_Item::GROUP_TYPE_NORMAL] + ] + ] + ], 'datatype' => 'dropdown' ]; @@ -377,11 +396,15 @@ public function showForm($ID, array $options = []) public function prepareInputForAdd($input) { + $this->prepareGroupFields($input); + return $this->prepareDefinitionInput($input); } public function prepareInputForUpdate($input) { + $this->prepareGroupFields($input); + return $this->prepareDefinitionInput($input); } diff --git a/src/Asset/AssetDefinitionManager.php b/src/Asset/AssetDefinitionManager.php index 1036e516fc0..96fbba2d40d 100644 --- a/src/Asset/AssetDefinitionManager.php +++ b/src/Asset/AssetDefinitionManager.php @@ -173,10 +173,7 @@ private function boostrapConcreteClass(AssetDefinition $definition): void // Register asset into configuration entries related to the capacities that cannot be disabled $config_keys = [ 'asset_types', - 'linkuser_types', - 'linkgroup_types', - 'linkuser_tech_types', - 'linkgroup_tech_types', + 'assignable_types', 'location_types', 'state_types', 'ticket_types', diff --git a/src/Cable.php b/src/Cable.php index 8a28130dce8..470bb17fcac 100644 --- a/src/Cable.php +++ b/src/Cable.php @@ -40,6 +40,7 @@ /// Class Cable class Cable extends CommonDBTM { + use Glpi\Features\AssignableItem; use Glpi\Features\Clonable; use Glpi\Features\State; @@ -271,6 +272,35 @@ public function rawSearchOptions() 'datatype' => 'text' ]; + $tab[] = [ + 'id' => '70', + 'table' => 'glpi_users', + 'field' => 'name', + 'name' => User::getTypeName(1), + 'datatype' => 'dropdown', + 'right' => 'all' + ]; + + $tab[] = [ + 'id' => '71', + 'table' => 'glpi_groups', + 'field' => 'completename', + 'name' => Group::getTypeName(1), + 'condition' => ['is_itemgroup' => 1], + 'joinparams' => [ + 'beforejoin' => [ + 'table' => 'glpi_groups_items', + 'joinparams' => [ + 'jointype' => 'itemtype_item', + 'condition' => ['NEWTABLE.type' => Group_Item::GROUP_TYPE_NORMAL] + ] + ] + ], + 'forcegroupby' => true, + 'massiveaction' => false, + 'datatype' => 'dropdown' + ]; + $tab[] = [ 'id' => '19', 'table' => $this->getTable(), @@ -286,6 +316,28 @@ public function rawSearchOptions() 'field' => 'name', 'linkfield' => 'users_id_tech', 'name' => __('Technician in charge'), + 'datatype' => 'dropdown', + 'right' => 'own_ticket' + ]; + + $tab[] = [ + 'id' => '49', + 'table' => 'glpi_groups', + 'field' => 'completename', + 'linkfield' => 'groups_id', + 'name' => __('Group in charge'), + 'condition' => ['is_assign' => 1], + 'joinparams' => [ + 'beforejoin' => [ + 'table' => 'glpi_groups_items', + 'joinparams' => [ + 'jointype' => 'itemtype_item', + 'condition' => ['NEWTABLE.type' => Group_Item::GROUP_TYPE_TECH] + ] + ] + ], + 'forcegroupby' => true, + 'massiveaction' => false, 'datatype' => 'dropdown' ]; diff --git a/src/CartridgeItem.php b/src/CartridgeItem.php index f9dc38790f9..768c06a93fa 100644 --- a/src/CartridgeItem.php +++ b/src/CartridgeItem.php @@ -37,7 +37,7 @@ use Glpi\Application\View\TemplateRenderer; use Glpi\DBAL\QueryFunction; use Glpi\Features\AssetImage; -use Glpi\Features\AssignableAsset; +use Glpi\Features\AssignableItem; /** * CartridgeItem Class @@ -47,7 +47,10 @@ class CartridgeItem extends CommonDBTM { use AssetImage; - use AssignableAsset; + use AssignableItem { + prepareInputForAdd as prepareInputForAddAssignableItem; + prepareInputForUpdate as prepareInputForUpdateAssignableItem; + } // From CommonDBTM protected static $forward_entity_to = ['Cartridge', 'Infocom']; @@ -76,13 +79,19 @@ public function getPostAdditionalInfosForName() public function prepareInputForAdd($input) { - $input = parent::prepareInputForAdd($input); + $input = $this->prepareInputForAddAssignableItem($input); + if ($input === false) { + return false; + } return $this->managePictures($input); } public function prepareInputForUpdate($input) { - $input = parent::prepareInputForUpdate($input); + $input = $this->prepareInputForUpdateAssignableItem($input); + if ($input === false) { + return false; + } return $this->managePictures($input); } @@ -295,9 +304,20 @@ public function rawSearchOptions() 'id' => '49', 'table' => 'glpi_groups', 'field' => 'completename', - 'linkfield' => 'groups_id_tech', + 'linkfield' => 'groups_id', 'name' => __('Group in charge'), 'condition' => ['is_assign' => 1], + 'joinparams' => [ + 'beforejoin' => [ + 'table' => 'glpi_groups_items', + 'joinparams' => [ + 'jointype' => 'itemtype_item', + 'condition' => ['NEWTABLE.type' => Group_Item::GROUP_TYPE_TECH] + ] + ] + ], + 'forcegroupby' => true, + 'massiveaction' => false, 'datatype' => 'dropdown' ]; diff --git a/src/Certificate.php b/src/Certificate.php index ba0d9163674..cfc76615622 100644 --- a/src/Certificate.php +++ b/src/Certificate.php @@ -33,23 +33,22 @@ * --------------------------------------------------------------------- */ -use Glpi\DBAL\QueryExpression; use Glpi\Application\View\TemplateRenderer; use Glpi\DBAL\QueryFunction; - -/** - * @since 9.2 - */ - - +use Glpi\Features\AssignableItem; /** * Class to declare a certificate + * @since 9.2 */ class Certificate extends CommonDBTM { use Glpi\Features\Clonable; use Glpi\Features\State; + use AssignableItem { + prepareInputForAdd as prepareInputForAddAssignableItem; + post_updateItem as post_updateItemAssignableItem; + } public $dohistory = true; public static $rightname = "certificate"; @@ -268,9 +267,20 @@ public function rawSearchOptions() 'id' => '49', 'table' => 'glpi_groups', 'field' => 'completename', - 'linkfield' => 'groups_id_tech', + 'linkfield' => 'groups_id', 'name' => __('Group in charge'), 'condition' => ['is_assign' => 1], + 'joinparams' => [ + 'beforejoin' => [ + 'table' => 'glpi_groups_items', + 'joinparams' => [ + 'jointype' => 'itemtype_item', + 'condition' => ['NEWTABLE.type' => Group_Item::GROUP_TYPE_TECH] + ] + ] + ], + 'forcegroupby' => true, + 'massiveaction' => false, 'datatype' => 'dropdown' ]; @@ -300,6 +310,17 @@ public function rawSearchOptions() 'field' => 'completename', 'name' => Group::getTypeName(1), 'condition' => ['is_itemgroup' => 1], + 'joinparams' => [ + 'beforejoin' => [ + 'table' => 'glpi_groups_items', + 'joinparams' => [ + 'jointype' => 'itemtype_item', + 'condition' => ['NEWTABLE.type' => Group_Item::GROUP_TYPE_NORMAL] + ] + ] + ], + 'forcegroupby' => true, + 'massiveaction' => false, 'datatype' => 'dropdown' ]; @@ -476,7 +497,10 @@ public function defineTabs($options = []) public function prepareInputForAdd($input) { - + $input = $this->prepareInputForAddAssignableItem($input); + if ($input === false) { + return false; + } if (isset($input["id"]) && ($input["id"] > 0)) { $input["_oldID"] = $input["id"]; } @@ -834,7 +858,7 @@ public static function getIcon() public function post_updateItem($history = true) { + $this->post_updateItemAssignableItem($history); $this->cleanAlerts([Alert::END]); - parent::post_updateItem($history); } } diff --git a/src/Cluster.php b/src/Cluster.php index a8f5f176966..fdae4385619 100644 --- a/src/Cluster.php +++ b/src/Cluster.php @@ -33,6 +33,8 @@ * --------------------------------------------------------------------- */ +use Glpi\Features\AssignableItem; + /** * Cluster Class **/ @@ -40,6 +42,7 @@ class Cluster extends CommonDBTM { use Glpi\Features\Clonable; use Glpi\Features\State; + use AssignableItem; // From CommonDBTM public $dohistory = true; @@ -146,9 +149,20 @@ public function rawSearchOptions() 'id' => '49', 'table' => 'glpi_groups', 'field' => 'completename', - 'linkfield' => 'groups_id_tech', + 'linkfield' => 'groups_id', 'name' => __('Group in charge'), 'condition' => ['is_assign' => 1], + 'joinparams' => [ + 'beforejoin' => [ + 'table' => 'glpi_groups_items', + 'joinparams' => [ + 'jointype' => 'itemtype_item', + 'condition' => ['NEWTABLE.type' => Group_Item::GROUP_TYPE_TECH] + ] + ] + ], + 'forcegroupby' => true, + 'massiveaction' => false, 'datatype' => 'dropdown' ]; diff --git a/src/CommonDBTM.php b/src/CommonDBTM.php index e9cad45509c..5a729fde2a1 100644 --- a/src/CommonDBTM.php +++ b/src/CommonDBTM.php @@ -38,6 +38,7 @@ use Glpi\DBAL\QueryFunction; use Glpi\DBAL\QueryParam; use Glpi\Event; +use Glpi\Features\AssignableItem; use Glpi\Features\CacheableListInterface; use Glpi\Plugin\Hooks; use Glpi\RichText\RichText; @@ -1005,6 +1006,17 @@ public function cleanRelationTable() */ global $CFG_GLPI, $DB; + if (in_array(static::class, $CFG_GLPI['assignable_types'], true)) { + $group_item = new Group_Item(); + $group_item->deleteByCriteria( + [ + 'itemtype' => static::class, + 'items_id' => $this->getID() + ], + true + ); + } + if (in_array(static::class, $CFG_GLPI['agent_types'], true)) { // Agent does not extends CommonDBConnexity $agent = new Agent(); @@ -3489,11 +3501,18 @@ public function getComments() $this->isField('groups_id') && ($this->getType() != 'Group') ) { - $tmp = Dropdown::getDropdownName("glpi_groups", $this->getField('groups_id')); - if ((strlen($tmp) != 0) && ($tmp != ' ')) { - $toadd[] = ['name' => Group::getTypeName(1), - 'value' => $tmp - ]; + $groups = $this->fields['groups_id']; + if (!is_array($groups)) { + $groups = [$groups]; + } + foreach ($groups as $group) { + $tmp = Dropdown::getDropdownName("glpi_groups", $group); + if ($tmp !== '' && $tmp !== ' ') { + $toadd[] = [ + 'name' => Group::getTypeName(1), + 'value' => $tmp + ]; + } } } diff --git a/src/CommonItilObject_Item.php b/src/CommonItilObject_Item.php index 9cb95e8a3f2..a2cf3196dd3 100644 --- a/src/CommonItilObject_Item.php +++ b/src/CommonItilObject_Item.php @@ -763,7 +763,7 @@ public static function dropdownMyDevices($userID = 0, $entity_restrict = -1, $it $devices = []; // My items - foreach ($CFG_GLPI["linkuser_types"] as $itemtype) { + foreach ($CFG_GLPI["assignable_types"] as $itemtype) { if ( ($item = getItemForItemtype($itemtype)) && CommonITILObject::isPossibleToAssignType($itemtype) @@ -851,19 +851,34 @@ public static function dropdownMyDevices($userID = 0, $entity_restrict = -1, $it $groups = array_merge($groups, $a_groups); } - foreach ($CFG_GLPI["linkgroup_types"] as $itemtype) { + foreach ($CFG_GLPI["assignable_types"] as $itemtype) { if ( ($item = getItemForItemtype($itemtype)) && CommonITILObject::isPossibleToAssignType($itemtype) ) { $itemtable = getTableForItemType($itemtype); $criteria = [ - 'FROM' => $itemtable, - 'WHERE' => [ - 'groups_id' => $groups + 'SELECT' => [$itemtable . '.*'], + 'FROM' => $itemtable, + 'LEFT JOIN' => [ + Group_Item::getTable() => [ + 'ON' => [ + $itemtable => 'id', + Group_Item::getTable() => 'items_id', [ + 'AND' => [ + Group_Item::getTable() . '.itemtype' => $itemtype + ] + ] + ] + ] + ], + 'WHERE' => [ + Group_Item::getTable() . '.type' => Group_Item::GROUP_TYPE_NORMAL, + Group_Item::getTable() . '.groups_id' => $groups ] + getEntitiesRestrictCriteria($itemtable, '', $entity_restrict, $item->maybeRecursive()) + $itemtype::getSystemSQLCriteria(), - 'ORDER' => $item->getNameField() + 'GROUPBY' => $itemtable . '.id', + 'ORDER' => $item->getNameField() ]; if ($item->maybeDeleted()) { diff --git a/src/Computer.php b/src/Computer.php index 8551be65def..be889b1f2c9 100644 --- a/src/Computer.php +++ b/src/Computer.php @@ -45,7 +45,10 @@ class Computer extends CommonDBTM use Glpi\Features\Clonable; use Glpi\Features\Inventoriable; use Glpi\Features\State; - use Glpi\Features\AssignableAsset; + use Glpi\Features\AssignableItem { + prepareInputForAdd as prepareInputForAddAssignableItem; + post_updateItem as post_updateItemAssignableItem; + } // From CommonDBTM public $dohistory = true; @@ -167,6 +170,8 @@ public function post_updateItem($history = true) */ global $CFG_GLPI, $DB; + $this->post_updateItemAssignableItem($history); + $changes = []; $update_count = count($this->updates ?? []); $input = $this->fields; @@ -185,12 +190,6 @@ public function post_updateItem($history = true) ) { $changes['users_id'] = $input['users_id']; } - if ( - $this->updates[$i] == 'groups_id' - && Entity::getUsedConfig('is_group_autoupdate', $this->getEntityID()) - ) { - $changes['groups_id'] = $input['groups_id']; - } // Update state of attached items if ( ($this->updates[$i] == 'states_id') @@ -207,6 +206,11 @@ public function post_updateItem($history = true) } } + // Group is handled differently since the field was changed to support multiple groups and was therefore moved to a separate table + if (array_key_exists('_groups_id', $this->input) && Entity::getUsedConfig('is_group_autoupdate', $this->getEntityID())) { + $changes['groups_id'] = $this->input['_groups_id']; + } + if (count($changes)) { $update_done = false; $is_input_dynamic = (bool) ($this->input['is_dynamic'] ?? false); @@ -312,13 +316,13 @@ public function post_updateItem($history = true) public function prepareInputForAdd($input) { - if (isset($input["id"]) && ($input["id"] > 0)) { $input["_oldID"] = $input["id"]; } unset($input['id']); unset($input['withtemplate']); + $input = $this->prepareInputForAddAssignableItem($input); return $input; } @@ -523,6 +527,17 @@ public function rawSearchOptions() 'field' => 'completename', 'name' => Group::getTypeName(1), 'condition' => ['is_itemgroup' => 1], + 'joinparams' => [ + 'beforejoin' => [ + 'table' => 'glpi_groups_items', + 'joinparams' => [ + 'jointype' => 'itemtype_item', + 'condition' => ['NEWTABLE.type' => Group_Item::GROUP_TYPE_NORMAL] + ] + ] + ], + 'forcegroupby' => true, + 'massiveaction' => false, 'datatype' => 'dropdown' ]; @@ -574,9 +589,20 @@ public function rawSearchOptions() 'id' => '49', 'table' => 'glpi_groups', 'field' => 'completename', - 'linkfield' => 'groups_id_tech', + 'linkfield' => 'groups_id', 'name' => __('Group in charge'), 'condition' => ['is_assign' => 1], + 'joinparams' => [ + 'beforejoin' => [ + 'table' => 'glpi_groups_items', + 'joinparams' => [ + 'jointype' => 'itemtype_item', + 'condition' => ['NEWTABLE.type' => Group_Item::GROUP_TYPE_TECH] + ] + ] + ], + 'forcegroupby' => true, + 'massiveaction' => false, 'datatype' => 'dropdown' ]; diff --git a/src/ConsumableItem.php b/src/ConsumableItem.php index 0cf8fb35414..c74162483c8 100644 --- a/src/ConsumableItem.php +++ b/src/ConsumableItem.php @@ -33,11 +33,11 @@ * --------------------------------------------------------------------- */ -use Glpi\DBAL\QueryExpression; use Glpi\Application\View\TemplateRenderer; +use Glpi\DBAL\QueryExpression; use Glpi\DBAL\QueryFunction; use Glpi\Features\AssetImage; -use Glpi\Features\AssignableAsset; +use Glpi\Features\AssignableItem; //! ConsumableItem Class /** @@ -50,7 +50,10 @@ class ConsumableItem extends CommonDBTM use Glpi\Features\Clonable; use AssetImage; - use AssignableAsset; + use AssignableItem { + prepareInputForAdd as prepareInputForAddAssignableItem; + prepareInputForUpdate as prepareInputForUpdateAssignableItem; + } // From CommonDBTM protected static $forward_entity_to = ['Consumable', 'Infocom']; @@ -92,13 +95,19 @@ public function getPostAdditionalInfosForName() public function prepareInputForAdd($input) { - $input = parent::prepareInputForAdd($input); + $input = $this->prepareInputForAddAssignableItem($input); + if ($input === false) { + return false; + } return $this->managePictures($input); } public function prepareInputForUpdate($input) { - $input = parent::prepareInputForUpdate($input); + $input = $this->prepareInputForUpdateAssignableItem($input); + if ($input === false) { + return false; + } return $this->managePictures($input); } @@ -254,9 +263,20 @@ public function rawSearchOptions() 'id' => '49', 'table' => 'glpi_groups', 'field' => 'completename', - 'linkfield' => 'groups_id_tech', + 'linkfield' => 'groups_id', 'name' => __('Group in charge'), 'condition' => ['is_assign' => 1], + 'joinparams' => [ + 'beforejoin' => [ + 'table' => 'glpi_groups_items', + 'joinparams' => [ + 'jointype' => 'itemtype_item', + 'condition' => ['NEWTABLE.type' => Group_Item::GROUP_TYPE_TECH] + ] + ] + ], + 'forcegroupby' => true, + 'massiveaction' => false, 'datatype' => 'dropdown' ]; diff --git a/src/DBmysql.php b/src/DBmysql.php index b1a681510c4..1438546d7a1 100644 --- a/src/DBmysql.php +++ b/src/DBmysql.php @@ -1351,7 +1351,7 @@ public function buildInsert($table, $params) * @since 9.3 * * @param string $table Table name - * @param array $params Query parameters ([field name => field value) + * @param QuerySubQuery|array $params Array of field => value pairs or a QuerySubQuery for INSERT INTO ... SELECT * * @return mysqli_result|boolean Query result handler */ diff --git a/src/DatabaseInstance.php b/src/DatabaseInstance.php index 529828a6e39..48653ef7dbf 100644 --- a/src/DatabaseInstance.php +++ b/src/DatabaseInstance.php @@ -34,12 +34,16 @@ */ use Glpi\Application\View\TemplateRenderer; +use Glpi\Features\AssignableItem; class DatabaseInstance extends CommonDBTM { use Glpi\Features\Clonable; use Glpi\Features\Inventoriable; use Glpi\Features\State; + use AssignableItem { + prepareInputForAdd as prepareInputForAddAssignableItem; + } // From CommonDBTM public $dohistory = true; @@ -130,6 +134,10 @@ public function showForm($ID, array $options = []) public function prepareInputForAdd($input) { + $input = $this->prepareInputForAddAssignableItem($input); + if ($input === false) { + return false; + } if (isset($input['date_lastbackup']) && empty($input['date_lastbackup'])) { unset($input['date_lastbackup']); } @@ -269,9 +277,20 @@ public function rawSearchOptions() 'id' => '49', 'table' => Group::getTable(), 'field' => 'completename', - 'linkfield' => 'groups_id_tech', + 'linkfield' => 'groups_id', 'name' => __('Group in charge'), 'condition' => ['is_assign' => 1], + 'joinparams' => [ + 'beforejoin' => [ + 'table' => 'glpi_groups_items', + 'joinparams' => [ + 'jointype' => 'itemtype_item', + 'condition' => ['NEWTABLE.type' => Group_Item::GROUP_TYPE_TECH] + ] + ] + ], + 'forcegroupby' => true, + 'massiveaction' => false, 'datatype' => 'dropdown' ]; diff --git a/src/Domain.php b/src/Domain.php index b53d2ca6c6b..1bc4189bbf3 100644 --- a/src/Domain.php +++ b/src/Domain.php @@ -34,12 +34,17 @@ */ use Glpi\DBAL\QueryExpression; -use Glpi\Toolbox\URL; +use Glpi\Features\AssignableItem; /// Class Domain class Domain extends CommonDBTM { use Glpi\Features\Clonable; + use AssignableItem { + prepareInputForAdd as prepareInputForAddAssignableItem; + prepareInputForUpdate as prepareInputForUpdateAssignableItem; + post_updateItem as post_updateItemAssignableItem; + } public static $rightname = 'domain'; protected static $forward_entity_to = ['DomainRecord']; @@ -171,9 +176,20 @@ public function rawSearchOptions() 'id' => '10', 'table' => 'glpi_groups', 'field' => 'name', - 'linkfield' => 'groups_id_tech', + 'linkfield' => 'groups_id', 'name' => __('Group in charge'), 'condition' => ['is_assign' => 1], + 'joinparams' => [ + 'beforejoin' => [ + 'table' => 'glpi_groups_items', + 'joinparams' => [ + 'jointype' => 'itemtype_item', + 'condition' => ['NEWTABLE.type' => Group_Item::GROUP_TYPE_TECH] + ] + ] + ], + 'forcegroupby' => true, + 'massiveaction' => false, 'datatype' => 'dropdown' ]; @@ -326,11 +342,19 @@ private function prepareInput($input) public function prepareInputForAdd($input) { + $input = $this->prepareInputForAddAssignableItem($input); + if ($input === false) { + return false; + } return $this->prepareInput($input); } public function prepareInputForUpdate($input) { + $input = $this->prepareInputForUpdateAssignableItem($input); + if ($input === false) { + return false; + } return $this->prepareInput($input); } @@ -871,7 +895,7 @@ public static function getIcon() public function post_updateItem($history = true) { + $this->post_updateItemAssignableItem($history); $this->cleanAlerts([Alert::END, Alert::NOTICE]); - parent::post_updateItem($history); } } diff --git a/src/DomainRecord.php b/src/DomainRecord.php index 36a3a8e024b..c5f17cd46a5 100644 --- a/src/DomainRecord.php +++ b/src/DomainRecord.php @@ -34,9 +34,15 @@ */ use Glpi\Application\View\TemplateRenderer; +use Glpi\Features\AssignableItem; class DomainRecord extends CommonDBChild { + use AssignableItem { + canUpdate as canUpdateAssignableItem; + canUpdateItem as canUpdateItemAssignableItem; + } + const DEFAULT_TTL = 3600; public static $rightname = 'domain'; @@ -144,8 +150,19 @@ public function rawSearchOptions() 'id' => '9', 'table' => 'glpi_groups', 'field' => 'name', - 'linkfield' => 'groups_id_tech', + 'linkfield' => 'groups_id', 'name' => __('Group in charge'), + 'joinparams' => [ + 'beforejoin' => [ + 'table' => 'glpi_groups_items', + 'joinparams' => [ + 'jointype' => 'itemtype_item', + 'condition' => ['NEWTABLE.type' => Group_Item::GROUP_TYPE_TECH] + ] + ] + ], + 'forcegroupby' => true, + 'massiveaction' => false, 'datatype' => 'dropdown' ]; @@ -179,6 +196,9 @@ public static function canCreate(): bool public static function canUpdate(): bool { + if (!self::canUpdateAssignableItem()) { + return false; + } if (count($_SESSION['glpiactiveprofile']['managed_domainrecordtypes'])) { return true; } @@ -208,6 +228,9 @@ public function canCreateItem(): bool public function canUpdateItem(): bool { + if (!$this->canUpdateItemAssignableItem()) { + return false; + } return parent::canUpdateItem() && ($_SESSION['glpiactiveprofile']['managed_domainrecordtypes'] === [-1] || in_array($this->fields['domainrecordtypes_id'], $_SESSION['glpiactiveprofile']['managed_domainrecordtypes'], true) @@ -304,11 +327,19 @@ private function prepareInput($input, $add = false) public function prepareInputForAdd($input) { + $input = $this->prepareGroupFields($input); + if ($input === false) { + return false; + } return $this->prepareInput($input, true); } public function prepareInputForUpdate($input) { + $input = $this->prepareGroupFields($input); + if ($input === false) { + return false; + } return $this->prepareInput($input); } diff --git a/src/Dropdown.php b/src/Dropdown.php index 1e68253ccef..c46c6de5841 100644 --- a/src/Dropdown.php +++ b/src/Dropdown.php @@ -39,7 +39,7 @@ use Glpi\DBAL\QueryExpression; use Glpi\DBAL\QueryFunction; use Glpi\Features\DCBreadcrumb; -use Glpi\Features\AssignableAsset; +use Glpi\Features\AssignableItem; use Glpi\Plugin\Hooks; use Glpi\SocketModel; @@ -2742,6 +2742,13 @@ public static function getDropdownValue($post, $json = true) $where = $item->getSystemSQLCriteria(); + if (Toolbox::hasTrait($post['itemtype'], AssignableItem::class)) { + $visibility_criteria = $post['itemtype']::getAssignableVisiblityCriteria(); + if (count($visibility_criteria)) { + $where[] = $visibility_criteria; + } + } + if ($item->maybeDeleted()) { $where["$table.is_deleted"] = 0; } @@ -3401,10 +3408,6 @@ public static function getDropdownValue($post, $json = true) } } - if (Toolbox::hasTrait($post['itemtype'], AssignableAsset::class)) { - $where[] = $post['itemtype']::getAssignableVisiblityCriteria(); - } - $criteria = array_merge( $criteria, [ @@ -3805,8 +3808,15 @@ public static function getDropdownFindNum($post, $json = true) return; } - $system_sql = $post['itemtype']::getSystemSQLCriteria(); - $where = !empty($system_sql) ? [$system_sql] : []; + $where = $item->getSystemSQLCriteria(); + + if (Toolbox::hasTrait($post['itemtype'], AssignableItem::class)) { + $visibility_criteria = $post['itemtype']::getAssignableVisiblityCriteria(); + if (count($visibility_criteria)) { + $where[] = $visibility_criteria; + } + } + if (isset($post['used']) && !empty($post['used'])) { $where['NOT'] = ['id' => $post['used']]; } @@ -3876,10 +3886,6 @@ public static function getDropdownFindNum($post, $json = true) $post['page_limit'] = $CFG_GLPI['dropdown_max']; } - if (Toolbox::hasTrait($post['itemtype'], AssignableAsset::class)) { - $where[] = $post['itemtype']::getAssignableVisiblityCriteria(); - } - $start = (int) (($post['page'] - 1) * $post['page_limit']); $limit = (int) $post['page_limit']; diff --git a/src/Enclosure.php b/src/Enclosure.php index 42bb2d82b84..c10f28047f1 100644 --- a/src/Enclosure.php +++ b/src/Enclosure.php @@ -34,6 +34,7 @@ */ use Glpi\Application\View\TemplateRenderer; +use Glpi\Features\AssignableItem; /** * Enclosure Class @@ -43,6 +44,9 @@ class Enclosure extends CommonDBTM use Glpi\Features\DCBreadcrumb; use Glpi\Features\Clonable; use Glpi\Features\State; + use AssignableItem { + prepareInputForAdd as prepareInputForAddAssignableItem; + } // From CommonDBTM public $dohistory = true; @@ -198,9 +202,20 @@ public function rawSearchOptions() 'id' => '49', 'table' => 'glpi_groups', 'field' => 'completename', - 'linkfield' => 'groups_id_tech', + 'linkfield' => 'groups_id', 'name' => __('Group in charge'), 'condition' => ['is_assign' => 1], + 'joinparams' => [ + 'beforejoin' => [ + 'table' => 'glpi_groups_items', + 'joinparams' => [ + 'jointype' => 'itemtype_item', + 'condition' => ['NEWTABLE.type' => Group_Item::GROUP_TYPE_TECH] + ] + ] + ], + 'forcegroupby' => true, + 'massiveaction' => false, 'datatype' => 'dropdown' ]; @@ -283,6 +298,10 @@ public function cleanDBonPurge() public function prepareInputForAdd($input) { + $input = $this->prepareInputForAddAssignableItem($input); + if ($input === false) { + return false; + } if (isset($input["id"]) && ($input["id"] > 0)) { $input["_oldID"] = $input["id"]; } diff --git a/src/Features/AssignableAsset.php b/src/Features/AssignableAsset.php deleted file mode 100644 index c165d5d215c..00000000000 --- a/src/Features/AssignableAsset.php +++ /dev/null @@ -1,149 +0,0 @@ -. - * - * --------------------------------------------------------------------- - */ - -namespace Glpi\Features; - -use Glpi\DBAL\QueryExpression; -use Session; - -trait AssignableAsset -{ - public static function canView(): bool - { - return Session::haveRightsOr(static::$rightname, [READ, READ_ASSIGNED, READ_OWNED]); - } - - public function canViewItem(): bool - { - if (!parent::canViewItem()) { - return false; - } - - $is_assigned = $this->fields['users_id_tech'] === $_SESSION['glpiID'] || - in_array((int) ($this->fields['groups_id_tech'] ?? 0), $_SESSION['glpigroups'] ?? [], true); - $is_owned = isset($this->fields['users_id']) && $this->fields['users_id'] === $_SESSION['glpiID'] || - in_array((int) ($this->fields['groups_id'] ?? 0), $_SESSION['glpigroups'] ?? [], true); - - if (!Session::haveRight(static::$rightname, READ)) { - return ($is_assigned && Session::haveRight(static::$rightname, READ_ASSIGNED)) - || ($is_owned && Session::haveRight(static::$rightname, READ_OWNED)); - } - - // Has global READ right - return true; - } - - public static function canUpdate(): bool - { - return Session::haveRightsOr(static::$rightname, [UPDATE, UPDATE_ASSIGNED, UPDATE_OWNED]); - } - - public function canUpdateItem(): bool - { - if (!parent::canUpdateItem()) { - return false; - } - - $is_assigned = $this->fields['users_id_tech'] === $_SESSION['glpiID'] || - in_array((int) ($this->fields['groups_id_tech'] ?? 0), $_SESSION['glpigroups'] ?? [], true); - $is_owned = isset($this->fields['users_id']) && $this->fields['users_id'] === $_SESSION['glpiID'] || - in_array((int) ($this->fields['groups_id'] ?? 0), $_SESSION['glpigroups'] ?? [], true); - - if (!Session::haveRight(static::$rightname, UPDATE)) { - return ($is_assigned && Session::haveRight(static::$rightname, UPDATE_ASSIGNED)) - || ($is_owned && Session::haveRight(static::$rightname, UPDATE_OWNED)); - } - - // Has global UPDATE right - return true; - } - - public static function getAssignableVisiblityCriteria(): array - { - if (!Session::haveRightsOr(static::$rightname, [READ, READ_ASSIGNED, READ_OWNED])) { - return [new QueryExpression('0')]; - } - - if (Session::haveRight(static::$rightname, READ)) { - return [new QueryExpression('1')]; - } - - $or = []; - if (Session::haveRight(static::$rightname, READ_ASSIGNED)) { - $or[] = [ - 'users_id_tech' => $_SESSION['glpiID'], - ]; - if (count($_SESSION['glpigroups'])) { - $or[] = [ - 'groups_id_tech' => $_SESSION['glpigroups'], - ]; - } - } - if (Session::haveRight(static::$rightname, READ_OWNED)) { - $or[] = [ - 'users_id' => $_SESSION['glpiID'], - ]; - if (count($_SESSION['glpigroups'])) { - $or[] = [ - 'groups_id' => $_SESSION['glpigroups'], - ]; - } - } - - // Add another layer to the array to prevent losing duplicates keys if the - // result of the function is merged with another array - $criteria = [crc32(serialize($or)) => ['OR' => $or]]; - - return $criteria; - } - - /** - * @param string $interface - * @phpstan-param 'central'|'helpdesk' $interface - * @return array - * @phpstan-return array - */ - public function getRights($interface = 'central') - { - $rights = parent::getRights($interface); - $rights[READ] = __('View all'); - $rights[READ_ASSIGNED] = __('View assigned'); - $rights[READ_OWNED] = __('View owned'); - $rights[UPDATE] = __('Update all'); - $rights[UPDATE_ASSIGNED] = __('Update assigned'); - $rights[UPDATE_OWNED] = __('Update owned'); - return $rights; - } -} diff --git a/src/Features/AssignableItem.php b/src/Features/AssignableItem.php new file mode 100644 index 00000000000..236795cc84e --- /dev/null +++ b/src/Features/AssignableItem.php @@ -0,0 +1,338 @@ +. + * + * --------------------------------------------------------------------- + */ + +namespace Glpi\Features; + +use Glpi\DBAL\QueryExpression; +use Glpi\DBAL\QuerySubQuery; +use Group_Item; +use Session; + +trait AssignableItem +{ + public static function canView(): bool + { + return Session::haveRightsOr(static::$rightname, [READ, READ_ASSIGNED, READ_OWNED]); + } + + public function canViewItem(): bool + { + if (!parent::canViewItem()) { + return false; + } + + $is_assigned = $this->fields['users_id_tech'] === $_SESSION['glpiID'] + || count(array_intersect($this->fields['groups_id_tech'] ?? [], $_SESSION['glpigroups'] ?? [])) > 0; + $is_owned = $this->fields['users_id'] === $_SESSION['glpiID'] + || count(array_intersect($this->fields['groups_id'] ?? [], $_SESSION['glpigroups'] ?? [])) > 0; + + if (!Session::haveRight(static::$rightname, READ)) { + return ($is_assigned && Session::haveRight(static::$rightname, READ_ASSIGNED)) + || ($is_owned && Session::haveRight(static::$rightname, READ_OWNED)); + } + + // Has global READ right + return true; + } + + public static function canUpdate(): bool + { + return Session::haveRightsOr(static::$rightname, [UPDATE, UPDATE_ASSIGNED, UPDATE_OWNED]); + } + + public function canUpdateItem(): bool + { + if (!parent::canUpdateItem()) { + return false; + } + + $is_assigned = $this->fields['users_id_tech'] === $_SESSION['glpiID'] + || count(array_intersect($this->fields['groups_id_tech'] ?? [], $_SESSION['glpigroups'] ?? [])) > 0; + $is_owned = $this->fields['users_id'] === $_SESSION['glpiID'] + || count(array_intersect($this->fields['groups_id'] ?? [], $_SESSION['glpigroups'] ?? [])) > 0; + + if (!Session::haveRight(static::$rightname, UPDATE)) { + return ($is_assigned && Session::haveRight(static::$rightname, UPDATE_ASSIGNED)) + || ($is_owned && Session::haveRight(static::$rightname, UPDATE_OWNED)); + } + + // Has global UPDATE right + return true; + } + + public static function getAssignableVisiblityCriteria(): array + { + if (!Session::haveRightsOr(static::$rightname, [READ, READ_ASSIGNED, READ_OWNED])) { + return [new QueryExpression('0')]; + } + + if (Session::haveRight(static::$rightname, READ)) { + return [new QueryExpression('1')]; + } + + $item_table = static::getTable(); + $relation_table = Group_Item::getTable(); + + $or = []; + if (Session::haveRight(static::$rightname, READ_ASSIGNED)) { + $or[] = [ + $item_table . '.users_id_tech' => $_SESSION['glpiID'], + ]; + if (count($_SESSION['glpigroups']) > 0) { + $or[] = [ + $item_table . '.id' => new QuerySubQuery([ + 'SELECT' => $relation_table . '.items_id', + 'FROM' => $relation_table, + 'WHERE' => [ + 'itemtype' => static::class, + 'groups_id' => $_SESSION['glpigroups'], + 'type' => Group_Item::GROUP_TYPE_TECH, + ] + ]) + ]; + } + } + if (Session::haveRight(static::$rightname, READ_OWNED)) { + $or[] = [ + $item_table . '.users_id' => $_SESSION['glpiID'], + ]; + if (count($_SESSION['glpigroups']) > 0) { + $or[] = [ + $item_table . '.id' => new QuerySubQuery([ + 'SELECT' => $relation_table . '.items_id', + 'FROM' => $relation_table, + 'WHERE' => [ + 'itemtype' => static::class, + 'groups_id' => $_SESSION['glpigroups'], + 'type' => Group_Item::GROUP_TYPE_NORMAL, + ] + ]) + ]; + } + } + + // Add another layer to the array to prevent losing duplicates keys if the + // result of the function is merged with another array + $criteria = [crc32(serialize($or)) => ['OR' => $or]]; + + return $criteria; + } + + /** + * @param string $interface + * @phpstan-param 'central'|'helpdesk' $interface + * @return array + * @phpstan-return array + */ + public function getRights($interface = 'central') + { + $rights = parent::getRights($interface); + $rights[READ] = __('View all'); + $rights[READ_ASSIGNED] = __('View assigned'); + $rights[READ_OWNED] = __('View owned'); + $rights[UPDATE] = __('Update all'); + $rights[UPDATE_ASSIGNED] = __('Update assigned'); + $rights[UPDATE_OWNED] = __('Update owned'); + return $rights; + } + + protected function prepareGroupFields(array $input) + { + $fields = ['groups_id', 'groups_id_tech']; + foreach ($fields as $field) { + if (array_key_exists($field, $input)) { + if (!is_array($input[$field])) { + $input[$field] = [$input[$field]]; + } + $input['_' . $field] = array_filter(array_map('intval', $input[$field] ?? []), static fn ($v) => $v > 0); + unset($input[$field]); + } + } + return $input; + } + + public function prepareInputForAdd($input): array|false + { + if ($input === false) { + return false; + } + $input = parent::prepareInputForAdd($input); + if ($input === false) { + return false; + } + return $this->prepareGroupFields($input); + } + + public function prepareInputForUpdate($input): array|false + { + if ($input === false) { + return false; + } + $input = parent::prepareInputForUpdate($input); + if ($input === false) { + return false; + } + return $this->prepareGroupFields($input); + } + + /** + * Update the values in the 'glpi_groups_items' link table as needed based on the groups set in the 'groups_id' and 'groups_id_tech' fields. + */ + private function updateGroupFields() + { + /** @var \DBmysql $DB */ + global $DB; + + // Find existing links + $existing_links = []; + if (!$this->isNewItem()) { + $it = $DB->request([ + 'SELECT' => ['id', 'groups_id', 'type'], + 'FROM' => 'glpi_groups_items', + 'WHERE' => [ + 'itemtype' => static::class, + 'items_id' => $this->getID(), + ], + ]); + $existing_links = iterator_to_array($it); + } + + // Group fields are changed to have a '_' prefix to avoid trying to update non-existent fields in the database + $fields = [ + Group_Item::GROUP_TYPE_NORMAL => '_groups_id', + Group_Item::GROUP_TYPE_TECH => '_groups_id_tech', + ]; + foreach ($fields as $type => $field) { + $existing_for_type = array_column(array_filter($existing_links, static fn($link) => $link['type'] === $type), 'groups_id'); + if (array_key_exists($field, $this->input)) { + $new_links = array_diff($this->input[$field], $existing_for_type); + $old_links = array_diff($existing_for_type, $this->input[$field]); + foreach ($new_links as $group_id) { + $group_item = new Group_Item(); + $group_item->add( + [ + 'itemtype' => static::class, + 'items_id' => $this->getID(), + 'groups_id' => $group_id, + 'type' => $type, + ] + ); + } + foreach ($old_links as $group_id) { + $group_item = new Group_Item(); + $group_item->deleteByCriteria( + [ + 'itemtype' => static::class, + 'items_id' => $this->getID(), + 'groups_id' => $group_id, + 'type' => $type, + ], + true + ); + } + } + } + + $this->loadGroupFields(); + } + + public function post_addItem() + { + parent::post_addItem(); + $this->updateGroupFields(); + } + + public function post_updateItem($history = true) + { + parent::post_updateItem($history); + $this->updateGroupFields(); + } + + public function getEmpty() + { + if (!parent::getEmpty()) { + return false; + } + $group_fields = ['groups_id', 'groups_id_tech']; + foreach ($group_fields as $field) { + $this->fields[$field] = []; + } + return true; + } + + private function loadGroupFields() + { + /** @var \DBmysql $DB */ + global $DB; + + // Find existing links + $it = $DB->request([ + 'SELECT' => ['id', 'groups_id', 'type'], + 'FROM' => 'glpi_groups_items', + 'WHERE' => [ + 'itemtype' => static::class, + 'items_id' => $this->getID(), + ], + ]); + $existing_links = iterator_to_array($it); + + $group_fields = [ + Group_Item::GROUP_TYPE_NORMAL => 'groups_id', + Group_Item::GROUP_TYPE_TECH => 'groups_id_tech', + ]; + foreach ($group_fields as $type => $field) { + if (in_array($type, $this->getGroupTypes(), true)) { + $this->fields[$field] = array_column(array_filter($existing_links, static fn($link) => $link['type'] === $type), 'groups_id'); + } + } + } + + public function post_getFromDB() + { + $this->loadGroupFields(); + } + + /** + * Get the types of groups supported by the asset. + * @return array + */ + final public function getGroupTypes(): array + { + return [ + Group_Item::GROUP_TYPE_NORMAL, + Group_Item::GROUP_TYPE_TECH, + ]; + } +} diff --git a/src/Features/Clonable.php b/src/Features/Clonable.php index 562b2b8e04d..8ad59789437 100644 --- a/src/Features/Clonable.php +++ b/src/Features/Clonable.php @@ -155,16 +155,21 @@ private function cloneRelations(CommonDBTM $source, bool $history): void /** * Prepare input datas for cloning the item. - * This empty method is meant to be redefined in objects that need a specific prepareInputForClone logic. - * - * @since 10.0.0 + * The default implementation handles specific cases when the class uses the following trait(s): + * - {@link AssignableItem} * * @param array $input datas used to add the item * * @return array the modified $input array + * @since 10.0.0 + * */ public function prepareInputForClone($input) { + if (method_exists($this, 'prepareGroupFields')) { + // Toolbox::hasTrait doesn't work to tell PHPStan this method exists even when using generics and assert-if-true + $input = $this->prepareGroupFields($input); + } return $input; } @@ -312,12 +317,17 @@ public function computeCloneName( /** * Post clone logic. - * This empty method is meant to be redefined in objects that need a specific post_clone logic. + * The default implementation handles specific cases when the class uses the following trait(s): + * - {@link AssignableItem} * * @param $source * @param $history */ public function post_clone($source, $history) { + if (method_exists($this, 'updateGroupFields')) { + // Toolbox::hasTrait doesn't work to tell PHPStan this method exists even when using generics and assert-if-true + $this->updateGroupFields(); + } } } diff --git a/src/Group.php b/src/Group.php index 2c64b5994f8..c273bcfc6fd 100644 --- a/src/Group.php +++ b/src/Group.php @@ -97,6 +97,7 @@ public function cleanDBonPurge() $this->deleteChildrenAndRelationsFromDb( [ Change_Group::class, + Group_Item::class, Group_KnowbaseItem::class, Group_Problem::class, Group_Reminder::class, @@ -526,8 +527,7 @@ public function showSecurityForm($ID) * * @since 0.83 * - * @param array $types Itemtypes - * @param string $field Field name + * @param bool $tech Whether to fetch items related to technician assignment or not. * @param boolean $tree Include child groups * @param boolean $user Include members (users) * @param integer $start First row to retrieve @@ -536,10 +536,16 @@ public function showSecurityForm($ID) * * @return integer total of items **/ - public function getDataItems(array $types, $field, $tree, $user, $start, array &$res, $extra_criteria = []) + public function getDataItems(bool $tech, bool $tree, bool $user, int $start, array &$res, array $extra_criteria = []): int { - /** @var \DBmysql $DB */ - global $DB; + /** + * @var \DBmysql $DB + * @var array $CFG_GLPI + */ + global $DB, $CFG_GLPI; + + $types = $CFG_GLPI['assignable_types']; + $ufield = $tech ? 'users_id_tech' : 'users_id'; // include item of child groups ? if ($tree) { @@ -547,33 +553,36 @@ public function getDataItems(array $types, $field, $tree, $user, $start, array & } else { $groups_ids = [$this->getID()]; } - // include items of members - $groups_criteria = []; + + $groups_criteria = [ + Group_Item::getTable() . '.groups_id' => $groups_ids, + Group_Item::getTable() . '.type' => $tech ? Group_Item::GROUP_TYPE_TECH : Group_Item::GROUP_TYPE_NORMAL + ]; + if ($user) { - $ufield = str_replace('groups', 'users', $field); - $groups_criteria['OR'] = [ - $field => $groups_ids, - [ - $field => 0, - $ufield => new QuerySubQuery( - [ - 'SELECT' => 'users_id', - 'FROM' => 'glpi_groups_users', - 'WHERE' => [ - 'groups_id' => $groups_ids, + // Get also items that are assigned to any user of the corresponding groups + $groups_criteria = [ + 'OR' => [ + $groups_criteria, + [ + $ufield => new QuerySubQuery( + [ + 'SELECT' => 'users_id', + 'FROM' => 'glpi_groups_users', + 'WHERE' => [ + 'groups_id' => $groups_ids, + ] ] - ] - ) + ) + ] ] ]; - } else { - $groups_criteria[$field] = $groups_ids; } // Count the total of item $nb = []; $tot = 0; - $savfield = $field; + $joins = []; $restrict = []; foreach ($types as $itemtype) { $nb[$itemtype] = 0; @@ -583,31 +592,21 @@ public function getDataItems(array $types, $field, $tree, $user, $start, array & if (!$item::canView()) { continue; } - if ($itemtype === 'Consumable') { - $field = 'items_id'; - } else { - $field = $savfield; - } - if (!$item->isField($field)) { - continue; - } - $restrict[$itemtype] = $groups_criteria + $item::getSystemSQLCriteria(); - if ($itemtype === 'Consumable') { - $restrict[$itemtype] = [ - $field => $groups_ids, - 'itemtype' => 'Group', - 'consumableitems_id' => new QuerySubQuery( - [ - 'SELECT' => 'id', - 'FROM' => 'glpi_consumableitems', - 'WHERE' => getEntitiesRestrictCriteria('glpi_consumableitems', '', '', true) + // Multiple groups + $joins[$itemtype][Group_Item::getTable()] = [ + 'ON' => [ + Group_Item::getTable() => 'items_id', + $item::getTable() => 'id', [ + 'AND' => [ + Group_Item::getTable() . '.itemtype' => $itemtype, ] - ), - ]; - } + ] + ] + ]; + $restrict[$itemtype] = $groups_criteria + $item::getSystemSQLCriteria(); - if ($item->isEntityAssign() && $itemtype !== 'Consumable') { + if ($item->isEntityAssign()) { $restrict[$itemtype] += getEntitiesRestrictCriteria( $item::getTable(), '', @@ -621,7 +620,10 @@ public function getDataItems(array $types, $field, $tree, $user, $start, array & if ($item->maybeDeleted()) { $restrict[$itemtype]['is_deleted'] = 0; } - $tot += $nb[$itemtype] = countElementsInTable($item::getTable(), $restrict[$itemtype]); + $tot += $nb[$itemtype] = countElementsInTable($item::getTable(), [ + 'LEFT JOIN' => $joins[$itemtype] ?? [], + 'WHERE' => $restrict[$itemtype] + ]); } $max = $_SESSION['glpilist_limit']; if ($start >= $tot) { @@ -637,14 +639,16 @@ public function getDataItems(array $types, $field, $tree, $user, $start, array & $start -= $nb[$itemtype]; } else { $request = [ - 'SELECT' => [ - $itemtype === 'Consumable' ? 'glpi_consumableitems.id' : 'id', + 'SELECT' => [ + $item::getTable() . '.id', new QueryExpression($DB::quoteValue($itemtype), 'itemtype') ], - 'FROM' => $item::getTable(), - 'WHERE' => $restrict[$itemtype], - 'LIMIT' => $max, - 'START' => $start + 'FROM' => $item::getTable(), + 'LEFT JOIN' => $joins[$itemtype] ?? [], + 'WHERE' => $restrict[$itemtype], + 'GROUPBY' => $item::getTable() . '.id', + 'LIMIT' => $max, + 'START' => $start ] + $extra_criteria; if ($item->isField('name')) { @@ -692,16 +696,7 @@ public function showItems($tech) $rand = mt_rand(); $ID = $this->fields['id']; - if ($tech) { - $types = $CFG_GLPI['linkgroup_tech_types']; - $field = 'groups_id_tech'; - } else { - $types = $CFG_GLPI['linkgroup_types']; - $field = 'groups_id'; - } - $tree = true; - $user = true; $datas = []; $start = (isset($_GET['start']) ? (int)$_GET['start'] : 0); $filters = $_GET['filters'] ?? []; @@ -715,7 +710,7 @@ public function showItems($tech) } } } - $nb = $this->getDataItems($types, $field, $tree, $user, $start, $datas, $extra_criteria); + $nb = $this->getDataItems($tech, true, true, $start, $datas, $extra_criteria); $show_massive_actions = false; @@ -756,19 +751,23 @@ public function showItems($tech) $entry['skip_ma'] = true; } - if ($tree || $user) { - if ($grp = $item->getField($field)) { + $assignees = []; + if ($grps = $item->getField($tech ? 'groups_id_tech' : 'groups_id')) { + foreach ($grps as $grp) { if (!isset($group_links[$grp]) && $group->getFromDB($grp)) { $group_links[$grp] = $group->getLink(['comments' => true]); } - $entry['field'] = $group_links[$grp] ?? ''; - } else if ($usr = $item->getField(str_replace('groups', 'users', $field))) { - if (!isset($user_links[$usr]) && $tuser->getFromDB($usr)) { - $user_links[$usr] = $tuser->getLink(['comments' => true]); - } - $entry['field'] = $user_links[$usr] ?? ''; + $assignees[] = $group_links[$grp] ?? ''; } } + if ($usr = $item->getField($tech ? 'users_id_tech' : 'users_id')) { + if (!isset($user_links[$usr]) && $tuser->getFromDB($usr)) { + $user_links[$usr] = $tuser->getLink(['comments' => true]); + } + $assignees[] = $user_links[$usr] ?? ''; + } + $entry['assignees'] = implode('
', array_filter($assignees)); + $entries[] = $entry; } @@ -781,11 +780,10 @@ public function showItems($tech) 'entity' => [ 'label' => Entity::getTypeName(1), 'no_filter' => true, - ] + ], + 'assignees' => sprintf(__s('%1$s / %2$s'), self::getTypeName(1), User::getTypeName(1)), ]; - if ($tree || $user) { - $columns['field'] = sprintf(__s('%1$s / %2$s'), self::getTypeName(1), User::getTypeName(1)); - } + TemplateRenderer::getInstance()->display('components/datatable.html.twig', [ 'is_tab' => true, 'start' => $start, @@ -794,7 +792,7 @@ public function showItems($tech) 'columns' => $columns, 'formatters' => [ 'name' => 'raw_html', - 'field' => 'raw_html' + 'assignees' => 'raw_html' ], 'entries' => $entries, 'total_number' => $nb, diff --git a/tests/units/Glpi/Features/AssignableAsset.php b/src/Group_Item.php similarity index 60% rename from tests/units/Glpi/Features/AssignableAsset.php rename to src/Group_Item.php index 98d8a717126..1ed2de6bf7c 100644 --- a/tests/units/Glpi/Features/AssignableAsset.php +++ b/src/Group_Item.php @@ -8,7 +8,6 @@ * http://glpi-project.org * * @copyright 2015-2024 Teclib' and contributors. - * @copyright 2003-2014 by the INDEPNET Development Team. * @licence https://www.gnu.org/licenses/gpl-3.0.html * * --------------------------------------------------------------------- @@ -33,31 +32,20 @@ * --------------------------------------------------------------------- */ -namespace tests\units\Glpi\Features; - -class AssignableAsset extends \DbTestCase +class Group_Item extends CommonDBRelation { - protected function itemtypeProvider() - { - /** - * @var array $CFG_GLPI - */ - global $CFG_GLPI; + public const GROUP_TYPE_NORMAL = 1; + public const GROUP_TYPE_TECH = 2; - foreach (['linkuser_types', 'linkgroup_types', 'linkuser_tech_types', 'linkgroup_tech_types'] as $cfg_key) { - foreach ($CFG_GLPI[$cfg_key] as $itemtype) { - yield[ - 'class' => $itemtype, - ]; - } - } - } + // From CommonDBRelation + public static $itemtype_1 = Group::class; + public static $items_id_1 = 'groups_id'; + + public static $itemtype_2 = 'itemtype'; + public static $items_id_2 = 'items_id'; - /** - * @dataProvider itemtypeProvider - */ - public function testClassUsesTrait($class) + public static function getTypeName($nb = 0) { - $this->boolean(in_array(\Glpi\Features\AssignableAsset::class, class_uses($class, true))); + return _n('Group item', 'Group items', $nb); } } diff --git a/src/Item_DeviceSimcard.php b/src/Item_DeviceSimcard.php index 5da5086a776..e5ff2a724e6 100644 --- a/src/Item_DeviceSimcard.php +++ b/src/Item_DeviceSimcard.php @@ -33,16 +33,16 @@ * --------------------------------------------------------------------- */ -/** - * @since 9.2 - */ - +use Glpi\Features\AssignableItem; /** * Relation between item and devices + * @since 9.2 **/ class Item_DeviceSimcard extends Item_Devices { + use AssignableItem; + public static $itemtype_2 = 'DeviceSimcard'; public static $items_id_2 = 'devicesimcards_id'; @@ -117,11 +117,23 @@ public static function getSpecificities($specif = '') 'datatype' => 'dropdown', 'dropdown_options' => ['right' => 'all'] ], - 'groups_id' => ['long name' => Group::getTypeName(1), + 'groups_id' => [ + 'long name' => Group::getTypeName(1), 'short name' => Group::getTypeName(1), 'size' => 20, 'id' => 22, - 'datatype' => 'dropdown' + 'joinparams' => [ + 'beforejoin' => [ + 'table' => 'glpi_groups_items', + 'joinparams' => [ + 'jointype' => 'itemtype_item', + 'condition' => ['NEWTABLE.type' => Group_Item::GROUP_TYPE_NORMAL] + ] + ] + ], + 'forcegroupby' => true, + 'massiveaction' => false, + 'datatype' => 'dropdown' ], 'users_id_tech' => [ 'long name' => __('Technician in charge'), @@ -136,7 +148,18 @@ public static function getSpecificities($specif = '') 'short name' => __('Group in charge'), 'size' => 20, 'id' => 24, - 'datatype' => 'dropdown', + 'joinparams' => [ + 'beforejoin' => [ + 'table' => 'glpi_groups_items', + 'joinparams' => [ + 'jointype' => 'itemtype_item', + 'condition' => ['NEWTABLE.type' => Group_Item::GROUP_TYPE_TECH] + ] + ] + ], + 'forcegroupby' => true, + 'massiveaction' => false, + 'datatype' => 'dropdown', 'dropdown_options' => ['condition' => ['is_assign' => 1]] ], ]; diff --git a/src/Item_Devices.php b/src/Item_Devices.php index 069b004cb29..005e9e7ba0b 100644 --- a/src/Item_Devices.php +++ b/src/Item_Devices.php @@ -76,6 +76,8 @@ class Item_Devices extends CommonDBRelation public static $mustBeAttached_2 = false; // Mandatory to display creation form + public static $rightname = 'device'; + protected function computeFriendlyName() { $itemtype = static::$itemtype_2; @@ -205,12 +207,21 @@ public function rawSearchOptions() 'table' => $table, 'field' => $field, 'name' => $attributs['long name'], - 'massiveaction' => true + 'massiveaction' => $attributs['massiveaction'] ?? true, ]; if (isset($attributs['datatype'])) { $newtab['datatype'] = $attributs['datatype']; } + if (isset($attributs['joinparams'])) { + $newtab['joinparams'] = $attributs['joinparams']; + } + if (isset($attributs['joinparams'])) { + $newtab['joinparams'] = $attributs['joinparams']; + } + if (isset($attributs['forcegroupby'])) { + $newtab['forcegroupby'] = $attributs['forcegroupby']; + } if (isset($attributs['nosearch'])) { $newtab['nosearch'] = $attributs['nosearch']; } diff --git a/src/Line.php b/src/Line.php index 36194f85c5f..fcfa057c5de 100644 --- a/src/Line.php +++ b/src/Line.php @@ -34,6 +34,7 @@ */ use Glpi\Application\View\TemplateRenderer; +use Glpi\Features\AssignableItem; /** * @since 9.2 @@ -43,6 +44,7 @@ class Line extends CommonDBTM { use Glpi\Features\State; + use AssignableItem; // From CommonDBTM public $dohistory = true; @@ -170,6 +172,17 @@ public function rawSearchOptions() 'field' => 'completename', 'name' => Group::getTypeName(1), 'condition' => ['is_itemgroup' => 1], + 'joinparams' => [ + 'beforejoin' => [ + 'table' => 'glpi_groups_items', + 'joinparams' => [ + 'jointype' => 'itemtype_item', + 'condition' => ['NEWTABLE.type' => Group_Item::GROUP_TYPE_NORMAL] + ] + ] + ], + 'forcegroupby' => true, + 'massiveaction' => false, 'datatype' => 'dropdown' ]; diff --git a/src/Link.php b/src/Link.php index f47f5c51e2c..a581e2042cc 100644 --- a/src/Link.php +++ b/src/Link.php @@ -36,6 +36,7 @@ use Glpi\Application\View\TemplateRenderer; use Glpi\ContentTemplates\TemplateManager; use Glpi\DBAL\QueryExpression; +use Glpi\Features\AssignableItem; use Glpi\Toolbox\URL; /** @@ -438,12 +439,24 @@ public static function generateLinkContents($link, CommonDBTM $item, bool $safe_ 'DOMAIN' => '', 'NETWORK' => $item->isField('networks_id') ? Dropdown::getDropdownName('glpi_networks', $item->getField('networks_id')) : '', 'USER' => $item->isField('users_id') ? Dropdown::getDropdownName('glpi_users', $item->getField('users_id')) : '', - 'GROUP' => $item->isField('groups_id') ? Dropdown::getDropdownName('glpi_groups', $item->getField('groups_id')) : '', 'REALNAME' => $item->isField('realname') ? $item->getField('realname') : '', 'FIRSTNAME' => $item->isField('firstname') ? $item->getField('firstname') : '', 'MODEL' => '', ]; + if (Toolbox::hasTrait($item::class, AssignableItem::class)) { + if (in_array(Group_Item::GROUP_TYPE_NORMAL, $item->getGroupTypes(), true)) { + $group_names = array_map(static fn ($group_id) => Dropdown::getDropdownName('glpi_groups', $group_id), $item->fields['groups_id']); + $vars['GROUPS'] = $group_names; + // GROUP - BC for < GLPI 11 + $vars['GROUP'] = count($group_names) > 0 ? array_shift($group_names) : ''; + } + } else { + $vars['GROUPS'] = []; + // GROUP - BC for < GLPI 11 + $vars['GROUP'] = $item->isField('groups_id') ? Dropdown::getDropdownName('glpi_groups', $item->getField('groups_id')) : ''; + } + $item_fields = $item->fields; $item::unsetUndisclosedFields($item_fields); if (count($item_fields)) { diff --git a/src/Monitor.php b/src/Monitor.php index 376f212c899..0b1dd693603 100644 --- a/src/Monitor.php +++ b/src/Monitor.php @@ -45,7 +45,9 @@ class Monitor extends CommonDBTM use Glpi\Features\Clonable; use Glpi\Features\Inventoriable; use Glpi\Features\State; - use Glpi\Features\AssignableAsset; + use Glpi\Features\AssignableItem { + prepareInputForAdd as prepareInputForAddAssignableItem; + } // From CommonDBTM public $dohistory = true; @@ -123,7 +125,6 @@ public function defineTabs($options = []) public function prepareInputForAdd($input) { - if (isset($input["id"]) && ($input["id"] > 0)) { $input["_oldID"] = $input["id"]; } @@ -133,6 +134,7 @@ public function prepareInputForAdd($input) unset($input['id']); unset($input['withtemplate']); + $input = $this->prepareInputForAddAssignableItem($input); return $input; } @@ -301,6 +303,17 @@ public function rawSearchOptions() 'field' => 'completename', 'name' => Group::getTypeName(1), 'condition' => ['is_itemgroup' => 1], + 'joinparams' => [ + 'beforejoin' => [ + 'table' => 'glpi_groups_items', + 'joinparams' => [ + 'jointype' => 'itemtype_item', + 'condition' => ['NEWTABLE.type' => Group_Item::GROUP_TYPE_NORMAL] + ] + ] + ], + 'forcegroupby' => true, + 'massiveaction' => false, 'datatype' => 'dropdown' ]; @@ -424,9 +437,20 @@ public function rawSearchOptions() 'id' => '49', 'table' => 'glpi_groups', 'field' => 'completename', - 'linkfield' => 'groups_id_tech', + 'linkfield' => 'groups_id', 'name' => __('Group in charge'), 'condition' => ['is_assign' => 1], + 'joinparams' => [ + 'beforejoin' => [ + 'table' => 'glpi_groups_items', + 'joinparams' => [ + 'jointype' => 'itemtype_item', + 'condition' => ['NEWTABLE.type' => Group_Item::GROUP_TYPE_TECH] + ] + ] + ], + 'forcegroupby' => true, + 'massiveaction' => false, 'datatype' => 'dropdown' ]; diff --git a/src/NetworkEquipment.php b/src/NetworkEquipment.php index 976e8f72b91..663b1ad1436 100644 --- a/src/NetworkEquipment.php +++ b/src/NetworkEquipment.php @@ -46,7 +46,9 @@ class NetworkEquipment extends CommonDBTM use Glpi\Features\Clonable; use Glpi\Features\Inventoriable; use Glpi\Features\State; - use Glpi\Features\AssignableAsset; + use Glpi\Features\AssignableItem { + prepareInputForAdd as prepareInputForAddAssignableItem; + } // From CommonDBTM public $dohistory = true; @@ -148,13 +150,13 @@ public function defineTabs($options = []) public function prepareInputForAdd($input) { - if (isset($input["id"]) && ($input["id"] > 0)) { $input["_oldID"] = $input["id"]; } unset($input['id']); unset($input['withtemplate']); + $input = $this->prepareInputForAddAssignableItem($input); return $input; } @@ -355,8 +357,19 @@ public function rawSearchOptions() 'table' => 'glpi_groups', 'field' => 'completename', 'name' => Group::getTypeName(1), - 'datatype' => 'dropdown', - 'condition' => ['is_itemgroup' => 1] + 'condition' => ['is_itemgroup' => 1], + 'joinparams' => [ + 'beforejoin' => [ + 'table' => 'glpi_groups_items', + 'joinparams' => [ + 'jointype' => 'itemtype_item', + 'condition' => ['NEWTABLE.type' => Group_Item::GROUP_TYPE_NORMAL] + ] + ] + ], + 'forcegroupby' => true, + 'massiveaction' => false, + 'datatype' => 'dropdown' ]; $tab[] = [ @@ -467,9 +480,20 @@ public function rawSearchOptions() 'id' => '49', 'table' => 'glpi_groups', 'field' => 'completename', - 'linkfield' => 'groups_id_tech', + 'linkfield' => 'groups_id', 'name' => __('Group in charge'), 'condition' => ['is_assign' => 1], + 'joinparams' => [ + 'beforejoin' => [ + 'table' => 'glpi_groups_items', + 'joinparams' => [ + 'jointype' => 'itemtype_item', + 'condition' => ['NEWTABLE.type' => Group_Item::GROUP_TYPE_TECH] + ] + ] + ], + 'forcegroupby' => true, + 'massiveaction' => false, 'datatype' => 'dropdown' ]; diff --git a/src/PDU.php b/src/PDU.php index 9b83e2f79fe..ff952fd20c6 100644 --- a/src/PDU.php +++ b/src/PDU.php @@ -38,6 +38,9 @@ **/ class PDU extends CommonDBTM { + use Glpi\Features\AssignableItem { + prepareInputForAdd as prepareInputForAddAssignableItem; + } use Glpi\Features\DCBreadcrumb; use Glpi\Features\Clonable; use Glpi\Features\State; @@ -127,6 +130,35 @@ public function rawSearchOptions() 'datatype' => 'text' ]; + $tab[] = [ + 'id' => '70', + 'table' => 'glpi_users', + 'field' => 'name', + 'name' => User::getTypeName(1), + 'datatype' => 'dropdown', + 'right' => 'all' + ]; + + $tab[] = [ + 'id' => '71', + 'table' => 'glpi_groups', + 'field' => 'completename', + 'name' => Group::getTypeName(1), + 'condition' => ['is_itemgroup' => 1], + 'joinparams' => [ + 'beforejoin' => [ + 'table' => 'glpi_groups_items', + 'joinparams' => [ + 'jointype' => 'itemtype_item', + 'condition' => ['NEWTABLE.type' => Group_Item::GROUP_TYPE_NORMAL] + ] + ] + ], + 'forcegroupby' => true, + 'massiveaction' => false, + 'datatype' => 'dropdown' + ]; + $tab[] = [ 'id' => '19', 'table' => $this->getTable(), @@ -175,9 +207,20 @@ public function rawSearchOptions() 'id' => '49', 'table' => 'glpi_groups', 'field' => 'completename', - 'linkfield' => 'groups_id_tech', + 'linkfield' => 'groups_id', 'name' => __('Group in charge'), 'condition' => ['is_assign' => 1], + 'joinparams' => [ + 'beforejoin' => [ + 'table' => 'glpi_groups_items', + 'joinparams' => [ + 'jointype' => 'itemtype_item', + 'condition' => ['NEWTABLE.type' => Group_Item::GROUP_TYPE_TECH] + ] + ] + ], + 'forcegroupby' => true, + 'massiveaction' => false, 'datatype' => 'dropdown' ]; @@ -244,6 +287,9 @@ public function prepareInputForAdd($input) } unset($input['id']); unset($input['withtemplate']); + + $input = $this->prepareInputForAddAssignableItem($input); + return $input; } } diff --git a/src/PassiveDCEquipment.php b/src/PassiveDCEquipment.php index 017cad90865..a4c9cd62f04 100644 --- a/src/PassiveDCEquipment.php +++ b/src/PassiveDCEquipment.php @@ -33,7 +33,6 @@ * --------------------------------------------------------------------- */ -use Glpi\Features\Clonable; use Glpi\Socket; /** @@ -41,7 +40,8 @@ **/ class PassiveDCEquipment extends CommonDBTM { - use Clonable; + use Glpi\Features\AssignableItem; + use Glpi\Features\Clonable; use Glpi\Features\DCBreadcrumb; use Glpi\Features\State; @@ -124,6 +124,35 @@ public function rawSearchOptions() $tab = array_merge($tab, Location::rawSearchOptionsToAdd()); + $tab[] = [ + 'id' => '70', + 'table' => 'glpi_users', + 'field' => 'name', + 'name' => User::getTypeName(1), + 'datatype' => 'dropdown', + 'right' => 'all' + ]; + + $tab[] = [ + 'id' => '71', + 'table' => 'glpi_groups', + 'field' => 'completename', + 'name' => Group::getTypeName(1), + 'condition' => ['is_itemgroup' => 1], + 'joinparams' => [ + 'beforejoin' => [ + 'table' => 'glpi_groups_items', + 'joinparams' => [ + 'jointype' => 'itemtype_item', + 'condition' => ['NEWTABLE.type' => Group_Item::GROUP_TYPE_NORMAL] + ] + ] + ], + 'forcegroupby' => true, + 'massiveaction' => false, + 'datatype' => 'dropdown' + ]; + $tab[] = [ 'id' => '19', 'table' => $this->getTable(), @@ -172,9 +201,20 @@ public function rawSearchOptions() 'id' => '49', 'table' => 'glpi_groups', 'field' => 'completename', - 'linkfield' => 'groups_id_tech', + 'linkfield' => 'groups_id', 'name' => __('Group in charge'), 'condition' => ['is_assign' => 1], + 'joinparams' => [ + 'beforejoin' => [ + 'table' => 'glpi_groups_items', + 'joinparams' => [ + 'jointype' => 'itemtype_item', + 'condition' => ['NEWTABLE.type' => Group_Item::GROUP_TYPE_TECH] + ] + ] + ], + 'forcegroupby' => true, + 'massiveaction' => false, 'datatype' => 'dropdown' ]; diff --git a/src/Peripheral.php b/src/Peripheral.php index ed8b82cf65f..6c65196373a 100644 --- a/src/Peripheral.php +++ b/src/Peripheral.php @@ -45,7 +45,9 @@ class Peripheral extends CommonDBTM use Glpi\Features\Clonable; use Glpi\Features\Inventoriable; use Glpi\Features\State; - use Glpi\Features\AssignableAsset; + use Glpi\Features\AssignableItem { + prepareInputForAdd as prepareInputForAddAssignableItem; + } // From CommonDBTM public $dohistory = true; @@ -129,12 +131,12 @@ public function defineTabs($options = []) public function prepareInputForAdd($input) { - if (isset($input["id"]) && ($input["id"] > 0)) { $input["_oldID"] = $input["id"]; } unset($input['id']); unset($input['withtemplate']); + $input = $this->prepareInputForAddAssignableItem($input); return $input; } @@ -273,6 +275,17 @@ public function rawSearchOptions() 'field' => 'completename', 'name' => Group::getTypeName(1), 'condition' => ['is_itemgroup' => 1], + 'joinparams' => [ + 'beforejoin' => [ + 'table' => 'glpi_groups_items', + 'joinparams' => [ + 'jointype' => 'itemtype_item', + 'condition' => ['NEWTABLE.type' => Group_Item::GROUP_TYPE_NORMAL] + ] + ] + ], + 'forcegroupby' => true, + 'massiveaction' => false, 'datatype' => 'dropdown' ]; @@ -332,9 +345,20 @@ public function rawSearchOptions() 'id' => '49', 'table' => 'glpi_groups', 'field' => 'completename', - 'linkfield' => 'groups_id_tech', + 'linkfield' => 'groups_id', 'name' => __('Group in charge'), 'condition' => ['is_assign' => 1], + 'joinparams' => [ + 'beforejoin' => [ + 'table' => 'glpi_groups_items', + 'joinparams' => [ + 'jointype' => 'itemtype_item', + 'condition' => ['NEWTABLE.type' => Group_Item::GROUP_TYPE_TECH] + ] + ] + ], + 'forcegroupby' => true, + 'massiveaction' => false, 'datatype' => 'dropdown' ]; diff --git a/src/Phone.php b/src/Phone.php index e2b63de0200..92c49059708 100644 --- a/src/Phone.php +++ b/src/Phone.php @@ -45,7 +45,9 @@ class Phone extends CommonDBTM use Glpi\Features\Clonable; use Glpi\Features\Inventoriable; use Glpi\Features\State; - use Glpi\Features\AssignableAsset; + use Glpi\Features\AssignableItem { + prepareInputForAdd as prepareInputForAddAssignableItem; + } // From CommonDBTM public $dohistory = true; @@ -136,13 +138,13 @@ public function defineTabs($options = []) public function prepareInputForAdd($input) { - if (isset($input["id"]) && ($input["id"] > 0)) { $input["_oldID"] = $input["id"]; } unset($input['id']); unset($input['withtemplate']); + $input = $this->prepareInputForAddAssignableItem($input); return $input; } @@ -327,6 +329,17 @@ public function rawSearchOptions() 'field' => 'completename', 'name' => Group::getTypeName(1), 'condition' => ['is_itemgroup' => 1], + 'joinparams' => [ + 'beforejoin' => [ + 'table' => 'glpi_groups_items', + 'joinparams' => [ + 'jointype' => 'itemtype_item', + 'condition' => ['NEWTABLE.type' => Group_Item::GROUP_TYPE_NORMAL] + ] + ] + ], + 'forcegroupby' => true, + 'massiveaction' => false, 'datatype' => 'dropdown' ]; @@ -414,9 +427,20 @@ public function rawSearchOptions() 'id' => '49', 'table' => 'glpi_groups', 'field' => 'completename', - 'linkfield' => 'groups_id_tech', + 'linkfield' => 'groups_id', 'name' => __('Group in charge'), 'condition' => ['is_assign' => 1], + 'joinparams' => [ + 'beforejoin' => [ + 'table' => 'glpi_groups_items', + 'joinparams' => [ + 'jointype' => 'itemtype_item', + 'condition' => ['NEWTABLE.type' => Group_Item::GROUP_TYPE_TECH] + ] + ] + ], + 'forcegroupby' => true, + 'massiveaction' => false, 'datatype' => 'dropdown' ]; diff --git a/src/Plugin.php b/src/Plugin.php index ede36fdc912..5f2c4fa40b8 100644 --- a/src/Plugin.php +++ b/src/Plugin.php @@ -1378,6 +1378,19 @@ public static function registerClass($itemtype, $attrib = []) return false; } + // Handle `linkuser_types`/`linkgroup_types`/`linkuser_tech_types`/`linkgroup_tech_types` deprecation + $is_assignable = false; + foreach (['linkuser_types', 'linkgroup_types', 'linkuser_tech_types', 'linkgroup_tech_types'] as $cfg_key) { + if (isset($attrib[$cfg_key]) && $attrib[$cfg_key]) { + Toolbox::deprecated(sprintf('`%s` configuration is deprecated, use `%s` instead.', $cfg_key, 'assignable_types')); + $is_assignable = true; + break; + } + } + if ($is_assignable) { + $attrib['assignable_types'] = true; + } + $all_types = preg_grep('/.+_types/', array_keys($CFG_GLPI)); $all_types[] = 'networkport_instantiations'; diff --git a/src/Printer.php b/src/Printer.php index 77a169a79b9..e6e0c1edc52 100644 --- a/src/Printer.php +++ b/src/Printer.php @@ -47,7 +47,10 @@ class Printer extends CommonDBTM use Glpi\Features\Clonable; use Glpi\Features\Inventoriable; use Glpi\Features\State; - use Glpi\Features\AssignableAsset; + use Glpi\Features\AssignableItem { + prepareInputForAdd as prepareInputForAddAssignableItem; + prepareInputForUpdate as prepareInputForUpdateAssignableItem; + } // From CommonDBTM public $dohistory = true; @@ -222,7 +225,6 @@ public function canUnrecurs() public function prepareInputForAdd($input) { - if (isset($input["id"]) && ($input["id"] > 0)) { $input["_oldID"] = $input["id"]; } @@ -240,13 +242,13 @@ public function prepareInputForAdd($input) $input['last_pages_counter'] = $input['init_pages_counter']; } + $input = $this->prepareInputForAddAssignableItem($input); return $input; } public function prepareInputForUpdate($input) { - if (isset($input['init_pages_counter'])) { $input['init_pages_counter'] = intval($input['init_pages_counter']); } @@ -254,6 +256,7 @@ public function prepareInputForUpdate($input) $input['last_pages_counter'] = intval($input['last_pages_counter']); } + $input = $this->prepareInputForUpdateAssignableItem($input); return $input; } @@ -435,6 +438,17 @@ public function rawSearchOptions() 'field' => 'completename', 'name' => Group::getTypeName(1), 'condition' => ['is_itemgroup' => 1], + 'joinparams' => [ + 'beforejoin' => [ + 'table' => 'glpi_groups_items', + 'joinparams' => [ + 'jointype' => 'itemtype_item', + 'condition' => ['NEWTABLE.type' => Group_Item::GROUP_TYPE_NORMAL] + ] + ] + ], + 'forcegroupby' => true, + 'massiveaction' => false, 'datatype' => 'dropdown' ]; @@ -619,9 +633,20 @@ public function rawSearchOptions() 'id' => '49', 'table' => 'glpi_groups', 'field' => 'completename', - 'linkfield' => 'groups_id_tech', + 'linkfield' => 'groups_id', 'name' => __('Group in charge'), 'condition' => ['is_assign' => 1], + 'joinparams' => [ + 'beforejoin' => [ + 'table' => 'glpi_groups_items', + 'joinparams' => [ + 'jointype' => 'itemtype_item', + 'condition' => ['NEWTABLE.type' => Group_Item::GROUP_TYPE_TECH] + ] + ] + ], + 'forcegroupby' => true, + 'massiveaction' => false, 'datatype' => 'dropdown' ]; diff --git a/src/Rack.php b/src/Rack.php index 8626d511ae9..c21effc665c 100644 --- a/src/Rack.php +++ b/src/Rack.php @@ -34,6 +34,7 @@ */ use Glpi\Application\View\TemplateRenderer; +use Glpi\Features\AssignableItem; /** * Rack Class @@ -42,6 +43,11 @@ class Rack extends CommonDBTM { use Glpi\Features\DCBreadcrumb; use Glpi\Features\State; + use AssignableItem { + prepareInputForAdd as prepareInputForAddAssignableItem; + prepareInputForUpdate as prepareInputForUpdateAssignableItem; + getEmpty as getEmptyAssignableItem; + } const FRONT = 0; const REAR = 1; @@ -265,9 +271,20 @@ public function rawSearchOptions() 'id' => '49', 'table' => 'glpi_groups', 'field' => 'completename', - 'linkfield' => 'groups_id_tech', + 'linkfield' => 'groups_id', 'name' => __('Group in charge'), 'condition' => ['is_assign' => 1], + 'joinparams' => [ + 'beforejoin' => [ + 'table' => 'glpi_groups_items', + 'joinparams' => [ + 'jointype' => 'itemtype_item', + 'condition' => ['NEWTABLE.type' => Group_Item::GROUP_TYPE_TECH] + ] + ] + ], + 'forcegroupby' => true, + 'massiveaction' => false, 'datatype' => 'dropdown' ]; @@ -305,6 +322,17 @@ public function rawSearchOptions() 'field' => 'completename', 'name' => Group::getTypeName(1), 'condition' => ['is_itemgroup' => 1], + 'joinparams' => [ + 'beforejoin' => [ + 'table' => 'glpi_groups_items', + 'joinparams' => [ + 'jointype' => 'itemtype_item', + 'condition' => ['NEWTABLE.type' => Group_Item::GROUP_TYPE_NORMAL] + ] + ] + ], + 'forcegroupby' => true, + 'massiveaction' => false, 'datatype' => 'dropdown' ]; @@ -741,6 +769,10 @@ classes: 'qtip-shadow qtip-bootstrap rack_tipcontent' public function prepareInputForAdd($input) { + $input = $this->prepareInputForAddAssignableItem($input); + if ($input === false) { + return false; + } if ($this->prepareInput($input)) { if (isset($input["id"]) && ($input["id"] > 0)) { $input["_oldID"] = $input["id"]; @@ -758,6 +790,10 @@ public function prepareInputForAdd($input) public function prepareInputForUpdate($input) { + $input = $this->prepareInputForUpdateAssignableItem($input); + if ($input === false) { + return false; + } if (array_key_exists('bgcolor', $input) && empty($input['bgcolor'])) { $input['bgcolor'] = '#FEC95C'; } @@ -909,7 +945,7 @@ public function getFilled($itemtype = null, $items_id = null) public function getEmpty() { - if (!parent::getEmpty()) { + if (!$this->getEmptyAssignableItem() || !parent::getEmpty()) { return false; } $this->fields['number_units'] = 42; diff --git a/src/RuleAsset.php b/src/RuleAsset.php index 1bbd21f6d2d..cb9052cc497 100644 --- a/src/RuleAsset.php +++ b/src/RuleAsset.php @@ -255,7 +255,11 @@ public function executeActions($output, $params, array $input = []) foreach ($this->actions as $action) { switch ($action->fields["action_type"]) { case "assign": - $output[$action->fields["field"]] = $action->fields["value"]; + if (in_array($action->fields['field'], ['groups_id', 'groups_id_tech'])) { + $output['_' . $action->fields["field"]] = [$action->fields["value"]]; + } else { + $output[$action->fields["field"]] = $action->fields["value"]; + } break; case "append": @@ -349,7 +353,7 @@ public function executeActions($output, $params, array $input = []) ($action->fields['field'] == 'groups_id') && isset($input['_default_groups_id_of_user']) ) { - $output['groups_id'] = $input['_default_groups_id_of_user']; + $output['_groups_id'] = [$input['_default_groups_id_of_user']]; } break; @@ -358,7 +362,7 @@ public function executeActions($output, $params, array $input = []) ($action->fields['field'] == 'groups_id') && isset($input['_groups_id_of_user']) ) { - $output['groups_id'] = reset($input['_groups_id_of_user']); + $output['_groups_id'] = count($input['_groups_id_of_user']) > 0 ? [reset($input['_groups_id_of_user'])] : []; } break; diff --git a/src/Search/Provider/SQLProvider.php b/src/Search/Provider/SQLProvider.php index ca91b136854..459cc9d901f 100644 --- a/src/Search/Provider/SQLProvider.php +++ b/src/Search/Provider/SQLProvider.php @@ -43,12 +43,13 @@ use Glpi\Asset\Asset_PeripheralAsset; use Glpi\Debug\Profiler; use Glpi\Form\Form; -use Glpi\Features\AssignableAsset; +use Glpi\Features\AssignableItem; use Glpi\RichText\RichText; use Glpi\Search\Input\QueryBuilder; use Glpi\Search\SearchEngine; use Glpi\Search\SearchOption; use Group; +use Group_Item; use ITILFollowup; use Problem; use Glpi\DBAL\QueryExpression; @@ -1000,9 +1001,12 @@ public static function getDefaultWhereCriteria(string $itemtype): array break; } - if (Toolbox::hasTrait($itemtype, AssignableAsset::class)) { - /** @var AssignableAsset $itemtype */ - $criteria[] = $itemtype::getAssignableVisiblityCriteria(); + if (Toolbox::hasTrait($itemtype, AssignableItem::class)) { + /** @var AssignableItem $itemtype */ + $visibility_criteria = $itemtype::getAssignableVisiblityCriteria(); + if (count($visibility_criteria)) { + $criteria[] = $visibility_criteria; + } } /* Hook to restrict user right on current itemtype */ @@ -2357,6 +2361,32 @@ public static function getLeftJoinCriteria( } $already_link_tables[] = $tocheck; + // Handle mixed group case for AllAssets and ReservationItem + if ($tocheck === 'glpi_groups' && ($itemtype === \AllAssets::class || $itemtype === \ReservationItem::class)) { + $already_link_tables[] = 'glpi_groups_items'; + return [ + 'LEFT JOIN' => [ + 'glpi_groups_items' => [ + 'ON' => [ + 'glpi_groups_items' => 'items_id', + $rt => 'id', [ + 'AND' => [ + 'glpi_groups_items.itemtype' => $rt . '_TYPE', // Placeholder to be replaced at the end of the SQL construction during union case handling + 'glpi_groups_items.type' => Group_Item::GROUP_TYPE_NORMAL, + ] + ] + ] + ], + 'glpi_groups' => [ + 'ON' => [ + 'glpi_groups' => 'id', + 'glpi_groups_items' => 'groups_id' + ] + ] + ] + ]; + } + $specific_leftjoin_criteria = []; // Plugin can override core definition for its type @@ -2990,7 +3020,7 @@ public static function getMetaLeftJoinCriteria(string $from_type, string $to_typ $from_table => 'id', [ 'AND' => [ - "$relation_table_alias." . 'itemtype_asset' => $asset_itemtype, + "$relation_table_alias." . 'itemtype_asset' => $asset_itemtype, "$relation_table_alias." . 'itemtype_peripheral' => $peripheral_itemtype, ] + $deleted_criteria ] @@ -3014,6 +3044,70 @@ public static function getMetaLeftJoinCriteria(string $from_type, string $to_typ return $joins; } + if ($to_type === 'Group' && in_array($from_referencetype, $CFG_GLPI['assignable_types'], true)) { + $relation_table_alias = 'glpi_groups_items' . $alias_suffix; + if (!in_array($relation_table_alias, $already_link_tables2, true)) { + $already_link_tables2[] = $relation_table_alias; + $joins['LEFT JOIN']["`glpi_groups_items` AS `$relation_table_alias`"] = [ + 'ON' => [ + $relation_table_alias => 'items_id', + $from_table => 'id', + [ + 'AND' => [ + $relation_table_alias . '.itemtype' => $from_referencetype, + $relation_table_alias . '.type' => Group_Item::GROUP_TYPE_NORMAL, + ] + ] + ] + ]; + } + if (!in_array($to_table_alias, $already_link_tables2, true)) { + $already_link_tables2[] = $to_table_alias; + $joins['LEFT JOIN'][$to_table_alias] = [ + 'ON' => [ + $to_table_alias => 'id', + $relation_table_alias => 'groups_id', + [ + 'AND' => $to_entity_restrict_criteria + $to_criteria + ] + ] + ]; + } + return $joins; + } + + if ($from_referencetype === 'Group' && in_array($to_type, $CFG_GLPI['assignable_types'], true)) { + $relation_table_alias = 'glpi_groups_items' . $alias_suffix; + if (!in_array($relation_table_alias, $already_link_tables2, true)) { + $already_link_tables2[] = $relation_table_alias; + $joins['LEFT JOIN']["`glpi_groups_items` AS `$relation_table_alias`"] = [ + 'ON' => [ + $relation_table_alias => 'groups_id', + $from_table => 'id', + [ + 'AND' => [ + $relation_table_alias . '.itemtype' => $to_type, + $relation_table_alias . '.type' => Group_Item::GROUP_TYPE_NORMAL, + ] + ] + ] + ]; + } + if (!in_array($to_table_alias, $already_link_tables2, true)) { + $already_link_tables2[] = $to_table_alias; + $joins['LEFT JOIN'][$to_table_join_id] = [ + 'ON' => [ + $relation_table_alias => 'items_id', + $to_table_alias => 'id', + [ + 'AND' => $to_entity_restrict_criteria + $to_criteria + ] + ] + ]; + } + return $joins; + } + // Generic JOIN $from_obj = getItemForItemtype($from_referencetype); $from_item_obj = null; @@ -4021,6 +4115,11 @@ public static function constructSQL(array &$data) $replace, $tmpquery ); + $tmpquery = str_replace( + $CFG_GLPI["union_search_type"][$data['itemtype']] . '_TYPE', + $ctype, + $tmpquery + ); $tmpquery = str_replace( $CFG_GLPI["union_search_type"][$data['itemtype']], $ctable, diff --git a/src/Search/SearchEngine.php b/src/Search/SearchEngine.php index 8264c3166fc..95b3242664e 100644 --- a/src/Search/SearchEngine.php +++ b/src/Search/SearchEngine.php @@ -180,10 +180,7 @@ private static function getMetaParentItemtypesForTypesConfig(string $config_key) 'appliance_types' => ['Appliance'], 'directconnect_types' => Asset_PeripheralAsset::getPeripheralHostItemtypes(), 'infocom_types' => ['Budget', 'Infocom'], - 'linkgroup_types' => ['Group'], - // 'linkgroup_tech_types' => ['Group'], // Cannot handle ambiguity with 'Group' from 'linkgroup_types' - 'linkuser_types' => ['User'], - // 'linkuser_tech_types' => ['User'], // Cannot handle ambiguity with 'User' from 'linkuser_types' + 'assignable_types' => ['Group', 'User'], 'project_asset_types' => ['Project'], 'rackable_types' => ['Enclosure', 'Rack'], 'socket_types' => [\Glpi\Socket::class], diff --git a/src/Software.php b/src/Software.php index d10d3e268ca..14d76c1629d 100644 --- a/src/Software.php +++ b/src/Software.php @@ -43,7 +43,11 @@ class Software extends CommonDBTM use Glpi\Features\Clonable; use Glpi\Features\TreeBrowse; use AssetImage; - use Glpi\Features\AssignableAsset; + use Glpi\Features\AssignableItem { + prepareInputForAdd as prepareInputForAddAssignableItem; + prepareInputForUpdate as prepareInputForUpdateAssignableItem; + getEmpty as getEmptyAssignableItem; + } // From CommonDBTM public $dohistory = true; @@ -126,7 +130,7 @@ public function prepareInputForUpdate($input) $input['softwares_id'] = 0; } $input = $this->managePictures($input); - return $input; + return $this->prepareInputForUpdateAssignableItem($input); } public function prepareInputForAdd($input) @@ -143,7 +147,7 @@ public function prepareInputForAdd($input) $this->handleCategoryRules($input); $input = $this->managePictures($input); - return $input; + return $this->prepareInputForAddAssignableItem($input); } public function cleanDBonPurge() @@ -211,7 +215,7 @@ public function getEmpty() /** @var array $CFG_GLPI */ global $CFG_GLPI; - if (!parent::getEmpty()) { + if (!$this->getEmptyAssignableItem()) { return false; } @@ -403,9 +407,20 @@ public function rawSearchOptions() 'id' => '49', 'table' => Group::getTable(), 'field' => 'completename', - 'linkfield' => 'groups_id_tech', - 'name' => __('Group in charge of the software'), + 'linkfield' => 'groups_id', + 'name' => __('Group in charge'), 'condition' => ['is_assign' => 1], + 'joinparams' => [ + 'beforejoin' => [ + 'table' => 'glpi_groups_items', + 'joinparams' => [ + 'jointype' => 'itemtype_item', + 'condition' => ['NEWTABLE.type' => Group_Item::GROUP_TYPE_TECH] + ] + ] + ], + 'forcegroupby' => true, + 'massiveaction' => false, 'datatype' => 'dropdown' ]; @@ -435,6 +450,17 @@ public function rawSearchOptions() 'field' => 'completename', 'name' => Group::getTypeName(1), 'condition' => ['is_itemgroup' => 1], + 'joinparams' => [ + 'beforejoin' => [ + 'table' => 'glpi_groups_items', + 'joinparams' => [ + 'jointype' => 'itemtype_item', + 'condition' => ['NEWTABLE.type' => Group_Item::GROUP_TYPE_NORMAL] + ] + ] + ], + 'forcegroupby' => true, + 'massiveaction' => false, 'datatype' => 'dropdown' ]; diff --git a/src/SoftwareLicense.php b/src/SoftwareLicense.php index 20ced6b2bbe..ef001460d0f 100644 --- a/src/SoftwareLicense.php +++ b/src/SoftwareLicense.php @@ -37,6 +37,7 @@ use Glpi\Application\View\TemplateRenderer; use Glpi\DBAL\QueryFunction; use Glpi\Features\AssetImage; +use Glpi\Features\AssignableItem; /** * SoftwareLicense Class @@ -46,6 +47,12 @@ class SoftwareLicense extends CommonTreeDropdown use Glpi\Features\Clonable; use Glpi\Features\State; use AssetImage; + use AssignableItem { + prepareInputForAdd as prepareInputForAddAssignableItem; + prepareInputForUpdate as prepareInputForUpdateAssignableItem; + post_addItem as post_addItemAssignableItem; + post_updateItem as post_updateItemAssignableItem; + } /// TODO move to CommonDBChild ? // From CommonDBTM @@ -87,6 +94,10 @@ public function pre_updateInDB() public function prepareInputForAdd($input) { + $input = $this->prepareInputForAddAssignableItem($input); + if ($input === false) { + return false; + } $input = parent::prepareInputForAdd($input); if (!isset($this->input['softwares_id']) || !$this->input['softwares_id']) { @@ -115,7 +126,10 @@ public function prepareInputForAdd($input) public function prepareInputForUpdate($input) { - $input = parent::prepareInputForUpdate($input); + $input = $this->prepareInputForUpdateAssignableItem($input); + if ($input === false) { + return false; + } // Update number : compute validity indicator if (isset($input['number'])) { @@ -184,6 +198,7 @@ public function cleanDBonPurge() public function post_addItem() { + $this->post_addItemAssignableItem(); // Add infocoms if exists for the licence $infocoms = Infocom::getItemsAssociatedTo(static::class, $this->fields['id']); if (!empty($infocoms)) { @@ -195,6 +210,7 @@ public function post_addItem() public function post_updateItem($history = true) { + $this->post_updateItemAssignableItem($history); if (in_array("is_valid", $this->updates, true)) { Software::updateValidityIndicator($this->fields["softwares_id"]); } @@ -456,9 +472,20 @@ public function rawSearchOptions() 'id' => '49', 'table' => Group::getTable(), 'field' => 'completename', - 'linkfield' => 'groups_id_tech', + 'linkfield' => 'groups_id', 'name' => __('Group in charge of the license'), 'condition' => ['is_assign' => 1], + 'joinparams' => [ + 'beforejoin' => [ + 'table' => 'glpi_groups_items', + 'joinparams' => [ + 'jointype' => 'itemtype_item', + 'condition' => ['NEWTABLE.type' => Group_Item::GROUP_TYPE_TECH] + ] + ] + ], + 'forcegroupby' => true, + 'massiveaction' => false, 'datatype' => 'dropdown' ]; @@ -488,6 +515,17 @@ public function rawSearchOptions() 'field' => 'completename', 'name' => Group::getTypeName(1), 'condition' => ['is_itemgroup' => 1], + 'joinparams' => [ + 'beforejoin' => [ + 'table' => 'glpi_groups_items', + 'joinparams' => [ + 'jointype' => 'itemtype_item', + 'condition' => ['NEWTABLE.type' => Group_Item::GROUP_TYPE_NORMAL] + ] + ] + ], + 'forcegroupby' => true, + 'massiveaction' => false, 'datatype' => 'dropdown' ]; diff --git a/src/Toolbox.php b/src/Toolbox.php index 87c67e1afba..a9d8711675b 100644 --- a/src/Toolbox.php +++ b/src/Toolbox.php @@ -3078,7 +3078,7 @@ public static function isValidWebUrl($url): bool * This function checks the class itself and all parent classes for the trait. * @since 10.0.0 * @param string|object $class The class or object - * @param string $trait The trait + * @param class-string $trait The trait * @return bool True if the class or its parents have the specified trait */ public static function hasTrait($class, string $trait): bool diff --git a/src/Unmanaged.php b/src/Unmanaged.php index a1fbc05dca4..fe41039462b 100644 --- a/src/Unmanaged.php +++ b/src/Unmanaged.php @@ -35,6 +35,7 @@ */ use Glpi\Application\View\TemplateRenderer; +use Glpi\Features\AssignableItem; /** * Not managed devices from inventory @@ -43,6 +44,7 @@ class Unmanaged extends CommonDBTM { use Glpi\Features\Inventoriable; use Glpi\Features\State; + use AssignableItem; // From CommonDBTM public $dohistory = true; @@ -194,6 +196,66 @@ public function rawSearchOptions() 'condition' => $this->getStateVisibilityCriteria() ]; + $tab[] = [ + 'id' => '24', + 'table' => User::getTable(), + 'field' => 'name', + 'linkfield' => 'users_id_tech', + 'name' => __('Technician in charge'), + 'datatype' => 'dropdown', + 'right' => 'own_ticket' + ]; + + $tab[] = [ + 'id' => '49', + 'table' => Group::getTable(), + 'field' => 'completename', + 'linkfield' => 'groups_id', + 'name' => __('Group in charge'), + 'condition' => ['is_assign' => 1], + 'joinparams' => [ + 'beforejoin' => [ + 'table' => 'glpi_groups_items', + 'joinparams' => [ + 'jointype' => 'itemtype_item', + 'condition' => ['NEWTABLE.type' => Group_Item::GROUP_TYPE_TECH] + ] + ] + ], + 'forcegroupby' => true, + 'massiveaction' => false, + 'datatype' => 'dropdown' + ]; + + $tab[] = [ + 'id' => '70', + 'table' => User::getTable(), + 'field' => 'name', + 'name' => User::getTypeName(1), + 'datatype' => 'dropdown', + 'right' => 'all' + ]; + + $tab[] = [ + 'id' => '71', + 'table' => Group::getTable(), + 'field' => 'completename', + 'name' => Group::getTypeName(1), + 'condition' => ['is_itemgroup' => 1], + 'joinparams' => [ + 'beforejoin' => [ + 'table' => 'glpi_groups_items', + 'joinparams' => [ + 'jointype' => 'itemtype_item', + 'condition' => ['NEWTABLE.type' => Group_Item::GROUP_TYPE_NORMAL] + ] + ] + ], + 'forcegroupby' => true, + 'massiveaction' => false, + 'datatype' => 'dropdown' + ]; + return $tab; } diff --git a/src/User.php b/src/User.php index 7bf39148d6a..c195efe14cd 100644 --- a/src/User.php +++ b/src/User.php @@ -5138,17 +5138,11 @@ public function showItems($tech) $start = intval($_GET["start"] ?? 0); if ($tech) { - $itemtypes = array_merge($CFG_GLPI['linkuser_tech_types'], $CFG_GLPI['linkgroup_tech_types']); $field_user = 'users_id_tech'; - $field_group = 'groups_id_tech'; } else { - $itemtypes = array_merge($CFG_GLPI['linkuser_types'], $CFG_GLPI['linkgroup_types']); $field_user = 'users_id'; - $field_group = 'groups_id'; } - $itemtypes = array_unique($itemtypes); - $group_where = ""; $groups = []; $iterator = $DB->request([ @@ -5169,27 +5163,52 @@ public function showItems($tech) ]); $number = 0; - $group_where = []; - foreach ($iterator as $data) { - $group_where[$field_group][] = $data['id']; - $groups[$data["id"]] = $data["name"]; + $criteria = [ + $field_user => $ID, + ]; + if ($iterator->count() > 0) { + $groups_ids = []; + foreach ($iterator as $data) { + $groups_ids[] = $data['id']; + $groups[$data["id"]] = $data["name"]; + } + $criteria = [ + 'OR' => [ + $criteria, + [ + Group_Item::getTable() . '.groups_id' => $groups_ids, + Group_Item::getTable() . '.type' => $tech ? Group_Item::GROUP_TYPE_TECH : Group_Item::GROUP_TYPE_NORMAL, + ] + ] + ]; } $entries = []; - foreach ($itemtypes as $itemtype) { + foreach ($CFG_GLPI['assignable_types'] as $itemtype) { if (!($item = getItemForItemtype($itemtype))) { continue; } - if ($item->canView()) { + if ($item::canView()) { $itemtable = getTableForItemType($itemtype); + $relation_table = Group_Item::getTable(); $iterator_params = [ - 'FROM' => $itemtable, - 'WHERE' => [ - 'OR' => [ - $field_user => $ID - ] + $group_where - ] + $item->getSystemSQLCriteria(), + 'SELECT' => ["$itemtable.*", "$relation_table.groups_id"], + 'FROM' => $itemtable, + 'LEFT JOIN' => [ + Group_Item::getTable() => [ + 'FKEY' => [ + $itemtable => 'id', + Group_Item::getTable() => 'items_id', [ + 'AND' => [ + Group_Item::getTable() . '.itemtype' => $itemtype, + ] + ] + ] + ] + ], + 'WHERE' => $criteria + $item::getSystemSQLCriteria(), + 'GROUPBY' => "$itemtable.id", ]; if ($item->maybeTemplate()) { @@ -5217,11 +5236,11 @@ public function showItems($tech) if ($data[$field_user] == $ID) { $linktypes[] = self::getTypeName(1); } - if (isset($groups[$data[$field_group]])) { + if (isset($groups[$data['groups_id']])) { $linktypes[] = sprintf( __('%1$s = %2$s'), Group::getTypeName(1), - $groups[$data[$field_group]] + $groups[$data['groups_id']] ); } if ($number >= $start && $number < $start + $_SESSION['glpilist_limit']) { @@ -5231,9 +5250,11 @@ public function showItems($tech) 'type' => $type_name, 'entity' => Dropdown::getDropdownName("glpi_entities", $data["entities_id"]), 'name' => $link, - 'serial' => $data["serial"], + 'serial' => $data["serial"] ?? '', 'otherserial' => $data["otherserial"], - 'states' => Dropdown::getDropdownName("glpi_states", $data['states_id'], false, true, false, ''), + 'states' => !empty($data['states_id']) + ? Dropdown::getDropdownName("glpi_states", $data['states_id'], false, true, false, '') + : '', 'linktype' => implode(', ', $linktypes), ]; } diff --git a/templates/components/form/item_device.html.twig b/templates/components/form/item_device.html.twig index cbe6a42b938..4538a891f98 100644 --- a/templates/components/form/item_device.html.twig +++ b/templates/components/form/item_device.html.twig @@ -217,6 +217,7 @@ field_options|merge({ 'entity': item.fields['entities_id'], 'condition': {'is_itemgroup': 1}, + 'multiple': item is usingtrait('Glpi\\Features\\AssignableItem') ? true : false }) ) }} {% endif %} diff --git a/templates/generic_show_form.html.twig b/templates/generic_show_form.html.twig index 66f899fe9ab..04bd7e460bd 100644 --- a/templates/generic_show_form.html.twig +++ b/templates/generic_show_form.html.twig @@ -399,7 +399,7 @@ ) }} {% endif %} - {% if item.isField('groups_id_tech') %} + {% if item.isField('groups_id_tech') or (item is usingtrait('Glpi\\Features\\AssignableItem') and item.GROUP_TYPE_TECH in item.groupTypes()) %} {{ fields.dropdownField( 'Group', 'groups_id_tech', @@ -408,6 +408,7 @@ field_options|merge({ 'entity': item.fields['entities_id'], 'condition': {'is_assign': 1}, + 'multiple': item is usingtrait('Glpi\\Features\\AssignableItem') ? true : false }) ) }} {% endif %} @@ -541,7 +542,7 @@ ) }} {% endif %} - {% if item.isField('groups_id') %} + {% if item.isField('groups_id') or (item is usingtrait('Glpi\\Features\\AssignableItem') and item.GROUP_TYPE_NORMAL in item.groupTypes()) %} {{ fields.dropdownField( 'Group', 'groups_id', @@ -550,6 +551,7 @@ field_options|merge({ 'entity': item.fields['entities_id'], 'condition': {'is_itemgroup': 1}, + 'multiple': item is usingtrait('Glpi\\Features\\AssignableItem') ? true : false }) ) }} {% endif %} diff --git a/tests/DbTestCase.php b/tests/DbTestCase.php index c101baffd27..f51b401ef6b 100644 --- a/tests/DbTestCase.php +++ b/tests/DbTestCase.php @@ -124,11 +124,13 @@ protected function checkInput(CommonDBTM $object, $id = 0, $input = []) if (count($input)) { foreach ($input as $k => $v) { + $obj_var = var_export($object->fields[$k], true); + $input_var = var_export($v, true); $this->variable($object->fields[$k])->isEqualTo( $v, " - '$k' key current value '{$object->fields[$k]}' (" . gettype($object->fields[$k]) . ") - is not equal to '$v' (" . gettype($v) . ")" + '$k' key current value '{$obj_var}' (" . gettype($object->fields[$k]) . ") + is not equal to '$input_var' (" . gettype($v) . ")" ); } } diff --git a/tests/GLPITestCase.php b/tests/GLPITestCase.php index cb96e45594b..40ad7d39849 100644 --- a/tests/GLPITestCase.php +++ b/tests/GLPITestCase.php @@ -364,4 +364,36 @@ protected function getTestRootEntity(bool $only_id = false) { return getItemByTypeName('Entity', '_test_root_entity', $only_id); } + + /** + * Return the minimal fields required for the creation of an item of the given field. + * + * @param string $class + * @return array + */ + protected function getMinimalCreationInput(string $class): array + { + if (!is_a($class, CommonDBTM::class, true)) { + return []; + } + + $input = []; + + if ((new $class())->isField('entities_id')) { + $input['entities_id'] = $this->getTestRootEntity(true); + } + + switch ($class) { + case Item_DeviceSimcard::class: + $input['itemtype'] = Computer::class; + $input['items_id'] = getItemByTypeName(Computer::class, '_test_pc01', true); + $input['devicesimcards_id'] = getItemByTypeName(DeviceSimcard::class, '_test_simcard_1', true); + break; + case SoftwareLicense::class: + $input['softwares_id'] = getItemByTypeName(Software::class, '_test_soft', true); + break; + } + + return $input; + } } diff --git a/tests/functional/Certificate.php b/tests/functional/Certificate.php index 6def43adf97..0ce1a4a994f 100644 --- a/tests/functional/Certificate.php +++ b/tests/functional/Certificate.php @@ -165,11 +165,11 @@ public function getIn($method = "") 'dns_name' => $this->getUniqueString(), 'dns_suffix' => $this->getUniqueString(), 'users_id_tech' => $this->getUniqueInteger(), - 'groups_id_tech' => $this->getUniqueInteger(), + 'groups_id_tech' => [$this->getUniqueInteger()], 'locations_id' => $this->getUniqueInteger(), 'manufacturers_id' => $this->getUniqueInteger(), 'users_id' => $this->getUniqueInteger(), - 'groups_id' => $this->getUniqueInteger(), + 'groups_id' => [$this->getUniqueInteger()], 'is_autosign' => 1, 'date_expiration' => date('Y-m-d', time() + MONTH_TIMESTAMP), 'states_id' => $this->getUniqueInteger(), diff --git a/tests/functional/CommonDBTM.php b/tests/functional/CommonDBTM.php index 17156c88556..5cb2f8a5ce8 100644 --- a/tests/functional/CommonDBTM.php +++ b/tests/functional/CommonDBTM.php @@ -1778,7 +1778,7 @@ protected function assignableAssetsProvider() /** * @dataProvider assignableAssetsProvider */ - public function testCanViewAssignableAssets($itemtype) + public function testCanViewAssignableItems($itemtype) { $this->login(); @@ -1794,7 +1794,7 @@ public function testCanViewAssignableAssets($itemtype) /** * @dataProvider assignableAssetsProvider */ - public function testCanViewItemAssignableAssets($itemtype) + public function testCanViewItemAssignableItems($itemtype) { $this->login(); @@ -1873,7 +1873,7 @@ public function testCanViewItemAssignableAssets($itemtype) /** * @dataProvider assignableAssetsProvider */ - public function testCanUpdateAssignableAssets($itemtype) + public function testCanUpdateAssignableItems($itemtype) { $this->login(); @@ -1889,7 +1889,7 @@ public function testCanUpdateAssignableAssets($itemtype) /** * @dataProvider assignableAssetsProvider */ - public function testCanUpdateItemAssignableAssets($itemtype) + public function testCanUpdateItemAssignableItems($itemtype) { $this->login(); diff --git a/tests/functional/Computer.php b/tests/functional/Computer.php index 8efd50befbd..cab8c1df354 100644 --- a/tests/functional/Computer.php +++ b/tests/functional/Computer.php @@ -114,10 +114,15 @@ public function testUpdate() $this->boolean($printer->getFromDB($printer->getID()))->isTrue(); unset($in['id']); foreach ($in as $k => $v) { - // Check the computer new values - $this->variable($computer->getField($k))->isEqualTo($v); - // Check the printer and test propagation occurs - $this->variable($printer->getField($k))->isEqualTo($v, $k); + $expected = $v; + if (in_array($k, ['groups_id', 'groups_id_tech'], true)) { + // These fields are transformed into arrays + $expected = [$v]; + } + // Check the computer new values + $this->variable($computer->getField($k))->isEqualTo($expected); + // Check the printer and test propagation occurs + $this->variable($printer->getField($k))->isEqualTo($expected, $k); } //reset values @@ -134,10 +139,15 @@ public function testUpdate() $this->boolean($printer->getFromDB($printer->getID()))->isTrue(); unset($in['id']); foreach ($in as $k => $v) { - // Check the computer new values - $this->variable($computer->getField($k))->isEqualTo($v); - // Check the printer and test propagation occurs - $this->variable($printer->getField($k))->isEqualTo($v); + $expected = $v; + if (in_array($k, ['groups_id', 'groups_id_tech'], true)) { + // These fields are transformed into arrays + $expected = $v === 0 ? [] : [$v]; + } + // Check the computer new values + $this->variable($computer->getField($k))->isEqualTo($expected); + // Check the printer and test propagation occurs + $this->variable($printer->getField($k))->isEqualTo($expected); } // Change the computer again @@ -163,10 +173,17 @@ public function testUpdate() $this->boolean($printer->getFromDB($printer->getID()))->isTrue(); unset($in2['id']); foreach ($in2 as $k => $v) { + $expected = $v; + $old_value = $in[$k]; + if (in_array($k, ['groups_id', 'groups_id_tech'], true)) { + // These fields are transformed into arrays + $expected = $v === 0 ? [] : [$v]; + $old_value = $old_value === 0 ? [] : [$old_value]; + } // Check the computer new values - $this->variable($computer->getField($k))->isEqualTo($v); + $this->variable($computer->getField($k))->isEqualTo($expected); // Check the printer and test propagation DOES NOT occurs - $this->variable($printer->getField($k))->isEqualTo($in[$k]); + $this->variable($printer->getField($k))->isEqualTo($old_value); } // Restore configuration @@ -211,10 +228,15 @@ public function testUpdate() $this->boolean($link->getFromDB($link->getID()))->isTrue(); unset($in['id']); foreach ($in as $k => $v) { - // Check the computer new values - $this->variable($computer->getField($k))->isEqualTo($v); - // Check the printer and test propagation occurs - $this->variable($link->getField($k))->isEqualTo($v); + $expected = $v; + if (in_array($k, ['groups_id', 'groups_id_tech'], true)) { + // These fields are transformed into arrays + $expected = [$v]; + } + // Check the computer new values + $this->variable($computer->getField($k))->isEqualTo($expected); + // Check the printer and test propagation occurs + $this->variable($link->getField($k))->isEqualTo($expected); } //reset @@ -248,10 +270,17 @@ public function testUpdate() $this->boolean($link->getFromDB($link->getID()))->isTrue(); unset($in2['id']); foreach ($in2 as $k => $v) { - // Check the computer new values - $this->variable($computer->getField($k))->isEqualTo($v); - // Check the printer and test propagation DOES NOT occurs - $this->variable($link->getField($k))->isEqualTo($in[$k]); + $old_value = $in[$k]; + $expected = $v; + if (in_array($k, ['groups_id', 'groups_id_tech'], true)) { + // These fields are transformed into arrays + $expected = [$v]; + $old_value = [$old_value]; + } + // Check the computer new values + $this->variable($computer->getField($k))->isEqualTo($expected); + // Check the printer and test propagation DOES NOT occurs + $this->variable($link->getField($k))->isEqualTo($old_value); } } @@ -312,10 +341,15 @@ public function testCreateLinks() $this->boolean($printer->getFromDB($printer->getID()))->isTrue(); unset($in['id']); foreach ($in as $k => $v) { - // Check the computer new values - $this->variable($computer->getField($k))->isEqualTo($v); - // Check the printer and test propagation occurs - $this->variable($printer->getField($k))->isEqualTo($v); + $expected = $v; + if (in_array($k, ['groups_id', 'groups_id_tech'], true)) { + // These fields are transformed into arrays + $expected = [$v]; + } + // Check the computer new values + $this->variable($computer->getField($k))->isEqualTo($expected); + // Check the printer and test propagation occurs + $this->variable($printer->getField($k))->isEqualTo($expected); } //create devices diff --git a/tests/functional/Dropdown.php b/tests/functional/Dropdown.php index 405776751c4..81fec5abced 100644 --- a/tests/functional/Dropdown.php +++ b/tests/functional/Dropdown.php @@ -40,7 +40,7 @@ use DbTestCase; use Generator; use Glpi\Features\Clonable; -use Glpi\Features\AssignableAsset; +use Glpi\Features\AssignableItem; use Glpi\Socket; use Item_DeviceSimcard; use Session; @@ -1945,11 +1945,11 @@ protected function assignableAssetsProvider() /** * @dataProvider assignableAssetsProvider */ - public function testGetDropdownValueAssignableAssets($itemtype) + public function testGetDropdownValueAssignableItems($itemtype) { $this->login(); - $this->boolean(\Toolbox::hasTrait($itemtype, AssignableAsset::class))->isTrue(); + $this->boolean(\Toolbox::hasTrait($itemtype, AssignableItem::class))->isTrue(); // Create group for the user $group = new \Group(); @@ -2035,11 +2035,11 @@ public function testGetDropdownValueAssignableAssets($itemtype) /** * @dataProvider assignableAssetsProvider */ - public function testGetDropdownFindNumAssignableAssets($itemtype) + public function testGetDropdownFindNumAssignableItems($itemtype) { $this->login(); - $this->boolean(\Toolbox::hasTrait($itemtype, AssignableAsset::class))->isTrue(); + $this->boolean(\Toolbox::hasTrait($itemtype, AssignableItem::class))->isTrue(); // Create group for the user $group = new \Group(); diff --git a/tests/functional/Glpi/Inventory/Inventory.php b/tests/functional/Glpi/Inventory/Inventory.php index 7bcdc954881..abce7d6b39b 100644 --- a/tests/functional/Glpi/Inventory/Inventory.php +++ b/tests/functional/Glpi/Inventory/Inventory.php @@ -83,7 +83,6 @@ private function checkComputer1($computers_id) 'contact' => 'trasher/root', 'contact_num' => null, 'users_id_tech' => 0, - 'groups_id_tech' => 0, 'comment' => null, 'date_mod' => $computer->fields['date_mod'], 'autoupdatesystems_id' => $autoupdatesystems_id, @@ -97,7 +96,6 @@ private function checkComputer1($computers_id) 'is_deleted' => 0, 'is_dynamic' => 1, 'users_id' => 0, - 'groups_id' => 0, 'states_id' => 0, 'ticket_tco' => '0.0000', 'uuid' => '4c4c4544-0034-3010-8048-b6c04f503732', @@ -105,6 +103,8 @@ private function checkComputer1($computers_id) 'is_recursive' => 0, 'last_inventory_update' => $computer->fields['last_inventory_update'], 'last_boot' => '2020-06-09 07:58:08', + 'groups_id' => [], + 'groups_id_tech' => [], ]; $this->array($computer->fields)->isIdenticalTo($expected); @@ -168,7 +168,6 @@ private function checkComputer1($computers_id) 'contact' => 'trasher/root', 'contact_num' => null, 'users_id_tech' => 0, - 'groups_id_tech' => 0, 'comment' => null, 'serial' => 'ABH55D', 'otherserial' => null, @@ -190,13 +189,14 @@ private function checkComputer1($computers_id) 'is_template' => 0, 'template_name' => null, 'users_id' => 0, - 'groups_id' => 0, 'states_id' => 0, 'ticket_tco' => '0.0000', 'is_dynamic' => 1, 'autoupdatesystems_id' => 0, 'uuid' => null, - 'is_recursive' => 0 + 'is_recursive' => 0, + 'groups_id' => [], + 'groups_id_tech' => [], ]; $this->array($monitor_fields)->isIdenticalTo($expected); @@ -869,7 +869,6 @@ private function checkComputer1($computers_id) 'contact' => 'trasher/root', 'contact_num' => null, 'users_id_tech' => 0, - 'groups_id_tech' => 0, 'serial' => 'MY47L1W1JHEB6', 'otherserial' => null, 'have_serial' => 0, @@ -891,7 +890,6 @@ private function checkComputer1($computers_id) 'init_pages_counter' => 0, 'last_pages_counter' => 0, 'users_id' => 0, - 'groups_id' => 0, 'states_id' => 0, 'ticket_tco' => '0.0000', 'is_dynamic' => 1, @@ -899,7 +897,9 @@ private function checkComputer1($computers_id) 'sysdescr' => null, 'last_inventory_update' => $_SESSION['glpi_currenttime'], 'snmpcredentials_id' => 0, - 'autoupdatesystems_id' => $autoupdatesystems_id + 'autoupdatesystems_id' => $autoupdatesystems_id, + 'groups_id' => [], + 'groups_id_tech' => [], ]; $this->array($printer_fields)->isIdenticalTo($expected); @@ -1278,7 +1278,6 @@ public function testUpdateComputer() 'contact' => 'johan', 'contact_num' => null, 'users_id_tech' => 0, - 'groups_id_tech' => 0, 'comment' => null, 'date_mod' => $computer->fields['date_mod'], 'autoupdatesystems_id' => $autoupdatesystems_id, @@ -1292,7 +1291,6 @@ public function testUpdateComputer() 'is_deleted' => 0, 'is_dynamic' => 1, 'users_id' => 0, - 'groups_id' => 0, 'states_id' => 0, 'ticket_tco' => '0.0000', 'uuid' => '0055ADC9-1D3A-E411-8043-B05D95113232', @@ -1300,6 +1298,8 @@ public function testUpdateComputer() 'is_recursive' => 0, 'last_inventory_update' => $computer->fields['last_inventory_update'], 'last_boot' => "2017-02-20 08:11:53", + 'groups_id' => [], + 'groups_id_tech' => [], ]; $this->array($computer->fields)->isIdenticalTo($expected); @@ -1498,7 +1498,6 @@ public function testUpdateComputer() 'contact' => 'johan', 'contact_num' => null, 'users_id_tech' => 0, - 'groups_id_tech' => 0, 'comment' => null, 'date_mod' => $computer->fields['date_mod'], 'autoupdatesystems_id' => $autoupdatesystems_id, @@ -1512,7 +1511,6 @@ public function testUpdateComputer() 'is_deleted' => 0, 'is_dynamic' => 1, 'users_id' => 0, - 'groups_id' => 0, 'states_id' => 0, 'ticket_tco' => '0.0000', 'uuid' => '0055ADC9-1D3A-E411-8043-B05D95113232', @@ -1520,6 +1518,8 @@ public function testUpdateComputer() 'is_recursive' => 0, 'last_inventory_update' => $computer->fields['last_inventory_update'], 'last_boot' => "2017-02-20 08:11:53", + 'groups_id' => [], + 'groups_id_tech' => [], ]; $this->array($computer->fields)->isIdenticalTo($expected); @@ -1666,7 +1666,6 @@ public function testUpdateComputer() 'contact' => 'johan', 'contact_num' => null, 'users_id_tech' => 0, - 'groups_id_tech' => 0, 'comment' => null, 'date_mod' => $computer->fields['date_mod'], 'autoupdatesystems_id' => $autoupdatesystems_id, @@ -1680,7 +1679,6 @@ public function testUpdateComputer() 'is_deleted' => 0, 'is_dynamic' => 1, 'users_id' => 0, - 'groups_id' => 0, 'states_id' => 0, 'ticket_tco' => '0.0000', 'uuid' => '0055ADC9-1D3A-E411-8043-B05D95113232', @@ -1688,6 +1686,8 @@ public function testUpdateComputer() 'is_recursive' => 0, 'last_inventory_update' => $computer->fields['last_inventory_update'], 'last_boot' => "2017-06-08 07:06:47", + 'groups_id' => [], + 'groups_id_tech' => [], ]; $this->array($computer->fields)->isIdenticalTo($expected); @@ -1935,7 +1935,6 @@ public function testImportNetworkEquipment() 'contact' => 'noc@glpi-project.org', 'contact_num' => null, 'users_id_tech' => 0, - 'groups_id_tech' => 0, 'date_mod' => $equipment->fields['date_mod'], 'comment' => null, 'locations_id' => $locations_id, @@ -1947,7 +1946,6 @@ public function testImportNetworkEquipment() 'is_template' => 0, 'template_name' => null, 'users_id' => 0, - 'groups_id' => 0, 'states_id' => 0, 'ticket_tco' => '0.0000', 'is_dynamic' => 1, @@ -1959,6 +1957,8 @@ public function testImportNetworkEquipment() 'uptime' => '482 days, 05:42:18.50', 'last_inventory_update' => $date_now, 'snmpcredentials_id' => 4, + 'groups_id' => [], + 'groups_id_tech' => [], ]; $this->array($equipment->fields)->isIdenticalTo($expected); @@ -2278,7 +2278,6 @@ public function testImportStackedNetworkEquipment() 'contact' => null, 'contact_num' => null, 'users_id_tech' => 0, - 'groups_id_tech' => 0, 'date_mod' => null, 'comment' => null, 'locations_id' => $locations_id, @@ -2290,7 +2289,6 @@ public function testImportStackedNetworkEquipment() 'is_template' => 0, 'template_name' => null, 'users_id' => 0, - 'groups_id' => 0, 'states_id' => 0, 'ticket_tco' => '0.0000', 'is_dynamic' => 1, @@ -2763,7 +2761,6 @@ public function testImportNetworkEquipmentMultiConnections() 'contact' => null, 'contact_num' => null, 'users_id_tech' => 0, - 'groups_id_tech' => 0, 'date_mod' => null, 'comment' => null, 'locations_id' => $locations_id, @@ -2775,7 +2772,6 @@ public function testImportNetworkEquipmentMultiConnections() 'is_template' => 0, 'template_name' => null, 'users_id' => 0, - 'groups_id' => 0, 'states_id' => 0, 'ticket_tco' => '0.0000', 'is_dynamic' => 1, @@ -3440,7 +3436,6 @@ public function testImportNetworkEquipmentWireless() 'contact' => null, 'contact_num' => null, 'users_id_tech' => 0, - 'groups_id_tech' => 0, 'date_mod' => null, 'comment' => null, 'locations_id' => $locations_id, @@ -3452,7 +3447,6 @@ public function testImportNetworkEquipmentWireless() 'is_template' => 0, 'template_name' => null, 'users_id' => 0, - 'groups_id' => 0, 'states_id' => 0, 'ticket_tco' => '0.0000', 'is_dynamic' => 1, @@ -3819,7 +3813,6 @@ public function testImportNetworkEquipmentWAggregatedPorts() 'contact' => 'noc@glpi-project.org', 'contact_num' => null, 'users_id_tech' => 0, - 'groups_id_tech' => 0, 'date_mod' => $equipment->fields['date_mod'], 'comment' => null, 'locations_id' => $locations_id, @@ -3831,7 +3824,6 @@ public function testImportNetworkEquipmentWAggregatedPorts() 'is_template' => 0, 'template_name' => null, 'users_id' => 0, - 'groups_id' => 0, 'states_id' => 0, 'ticket_tco' => '0.0000', 'is_dynamic' => 1, @@ -3843,6 +3835,8 @@ public function testImportNetworkEquipmentWAggregatedPorts() 'uptime' => '65 days, 20:13:08.93', 'last_inventory_update' => $date_now, 'snmpcredentials_id' => 0, + 'groups_id' => [], + 'groups_id_tech' => [], ]; $this->array($equipment->fields)->isIdenticalTo($expected); @@ -5183,7 +5177,6 @@ public function testImportPhone() 'contact' => 'builder', 'contact_num' => null, 'users_id_tech' => 0, - 'groups_id_tech' => 0, 'comment' => null, 'serial' => 'af8d8fcfa6fa4794', 'otherserial' => 'release-keys', @@ -5201,7 +5194,6 @@ public function testImportPhone() 'is_template' => 0, 'template_name' => null, 'users_id' => 0, - 'groups_id' => 0, 'states_id' => 0, 'ticket_tco' => '0.0000', 'is_dynamic' => 1, @@ -5210,6 +5202,8 @@ public function testImportPhone() 'date_creation' => $phone->fields['date_creation'], 'is_recursive' => 0, 'last_inventory_update' => $phone->fields['last_inventory_update'], + 'groups_id' => [], + 'groups_id_tech' => [], ]; $this->array($phone->fields)->isIdenticalTo($expected); @@ -5537,9 +5531,7 @@ public function testImportPhone() 'locations_id' => 0, 'lines_id' => 0, 'users_id' => 0, - 'groups_id' => 0, 'users_id_tech' => 0, - 'groups_id_tech' => 0, 'pin' => '', 'pin2' => '', 'puk' => '', diff --git a/tests/functional/Link.php b/tests/functional/Link.php index e6200dc2fba..5f6284e9518 100644 --- a/tests/functional/Link.php +++ b/tests/functional/Link.php @@ -65,7 +65,8 @@ protected function linkContentProvider(): iterable 'networks_id' => $network->getID(), 'users_id' => getItemByTypeName(\User::class, 'glpi', true), 'computermodels_id' => getItemByTypeName(\ComputerModel::class, '_test_computermodel_1', true), - ] + ], + ['groups_id'] ); // Attach domains diff --git a/tests/functional/Monitor.php b/tests/functional/Monitor.php index 55ac7b8c2bf..329aa6ce1e1 100644 --- a/tests/functional/Monitor.php +++ b/tests/functional/Monitor.php @@ -51,7 +51,6 @@ private static function getMonitorFields($id, $date) 'contact' => null, 'contact_num' => null, 'users_id_tech' => 0, - 'groups_id_tech' => 0, 'comment' => null, 'serial' => null, 'otherserial' => null, @@ -73,7 +72,6 @@ private static function getMonitorFields($id, $date) 'is_template' => 0, 'template_name' => null, 'users_id' => 0, - 'groups_id' => 0, 'states_id' => 0, 'ticket_tco' => '0.0000', 'is_dynamic' => 0, @@ -81,6 +79,8 @@ private static function getMonitorFields($id, $date) 'date_creation' => $date, 'is_recursive' => 0, 'uuid' => null, + 'groups_id' => [], + 'groups_id_tech' => [], ]; } diff --git a/tests/functional/Search.php b/tests/functional/Search.php index 1868bc4f2ab..5dd34b22c74 100644 --- a/tests/functional/Search.php +++ b/tests/functional/Search.php @@ -807,10 +807,12 @@ public function testIsNotifyComputerGroup() $search_params = ['is_deleted' => 0, 'start' => 0, 'search' => 'Search', - 'criteria' => [0 => ['field' => 'view', - 'searchtype' => 'contains', - 'value' => '' - ] + 'criteria' => [ + 0 => [ + 'field' => 'view', + 'searchtype' => 'contains', + 'value' => '' + ] ], // group is_notify 'metacriteria' => [0 => ['link' => 'AND', @@ -1898,10 +1900,8 @@ public function testAllAssetsFields() 'users_id', 'contact', 'contact_num', - 'groups_id', 'date_mod', 'manufacturers_id', - 'groups_id_tech', 'entities_id', ]; diff --git a/tests/units/Glpi/Features/AssignableItem.php b/tests/units/Glpi/Features/AssignableItem.php new file mode 100644 index 00000000000..168fb54fb85 --- /dev/null +++ b/tests/units/Glpi/Features/AssignableItem.php @@ -0,0 +1,245 @@ +. + * + * --------------------------------------------------------------------- + */ + +namespace tests\units\Glpi\Features; + +use Group_Item; + +class AssignableItem extends \DbTestCase +{ + protected function itemtypeProvider(): iterable + { + /** + * @var array $CFG_GLPI + */ + global $CFG_GLPI; + + foreach ($CFG_GLPI['assignable_types'] as $itemtype) { + yield $itemtype => [ + 'class' => $itemtype, + ]; + } + } + + /** + * @dataProvider itemtypeProvider + */ + public function testClassUsesTrait(string $class): void + { + $this->boolean(in_array(\Glpi\Features\AssignableItem::class, class_uses($class), true))->isTrue(); + } + + protected function groupAssignableItemtypeProvider(): iterable + { + /** + * @var array $CFG_GLPI + */ + global $CFG_GLPI; + + foreach ($CFG_GLPI['assignable_types'] as $itemtype) { + yield $itemtype . '-normal' => [ + 'class' => $itemtype, + 'type' => Group_Item::GROUP_TYPE_NORMAL, + ]; + + yield $itemtype . '-normal' => [ + 'class' => $itemtype, + 'type' => Group_Item::GROUP_TYPE_TECH, + ]; + } + } + + /** + * Test adding an item with the groups_id/groups_id_tech field as an array and null. + * Test updating an item with the groups_id/groups_id_tech field as an array and null. + * + * @dataProvider itemtypeProvider + */ + public function testAddAndUpdateMultipleGroups(string $class): void + { + $this->login(); // login to bypass some rights checks (e.g. on domain records) + + $input = $this->getMinimalCreationInput($class); + + $item_1 = $this->createItem( + $class, + $input + [ + $class::getNameField() => __FUNCTION__ . ' 1', + 'groups_id' => [1, 2], + 'groups_id_tech' => [3], + ] + ); + $this->array($item_1->fields['groups_id'])->isEqualTo([1, 2]); + $this->array($item_1->fields['groups_id_tech'])->isEqualTo([3]); + + $item_2 = $this->createItem( + $class, + $input + [ + $class::getNameField() => __FUNCTION__ . ' 2', + 'groups_id' => null, + 'groups_id_tech' => null, + ] + ); + $this->array($item_2->fields['groups_id'])->isEmpty(); + $this->array($item_2->fields['groups_id_tech'])->isEmpty(); + + // Update both items. Asset 1 will have the groups set to null and item 2 will have the groups set to an array. + $updated = $item_1->update(['id' => $item_1->getID(), 'groups_id' => null, 'groups_id_tech' => null]); + $this->boolean($updated)->isTrue(); + $this->array($item_1->fields['groups_id'])->isEmpty(); + $this->array($item_1->fields['groups_id_tech'])->isEmpty(); + + $updated = $item_2->update(['id' => $item_2->getID(), 'groups_id' => [5, 6], 'groups_id_tech' => [7]]); + $this->boolean($updated)->isTrue(); + $this->array($item_2->fields['groups_id'])->isEqualTo([5, 6]); + $this->array($item_2->fields['groups_id_tech'])->isEqualTo([7]); + + // Test updating array to array + $updated = $item_2->update(['id' => $item_2->getID(), 'groups_id' => [1, 2], 'groups_id_tech' => [4, 5]]); + $this->boolean($updated)->isTrue(); + $this->array($item_2->fields['groups_id'])->isEqualTo([1, 2]); + $this->array($item_2->fields['groups_id_tech'])->isEqualTo([4, 5]); + } + + /** + * Test the loading item which still have integer values for groups_id/groups_id_tech (0 for no group). + * The value should be automatically normalized to an array. If the group was '0', the array should be empty. + * + * @dataProvider itemtypeProvider + */ + public function testLoadGroupsFromDb(string $class): void + { + /** @var \DBmysql $DB */ + global $DB; + + $input = $this->getMinimalCreationInput($class); + + $item = $this->createItem( + $class, + $input + [ + $class::getNameField() => __FUNCTION__, + ] + ); + $this->array($item->fields['groups_id'])->isEmpty(); + $this->array($item->fields['groups_id_tech'])->isEmpty(); + + $DB->insert( + 'glpi_groups_items', + [ + 'itemtype' => $class, + 'items_id' => $item->getID(), + 'groups_id' => 1, + 'type' => Group_Item::GROUP_TYPE_NORMAL, + ], + ); + $DB->insert( + 'glpi_groups_items', + [ + 'itemtype' => $class, + 'items_id' => $item->getID(), + 'groups_id' => 2, + 'type' => Group_Item::GROUP_TYPE_TECH, + ], + ); + + $this->boolean($item->getFromDB($item->getID()))->isTrue(); + $this->array($item->fields['groups_id'])->isEqualTo([1]); + $this->array($item->fields['groups_id_tech'])->isEqualTo([2]); + + $DB->insert( + 'glpi_groups_items', + [ + 'itemtype' => $class, + 'items_id' => $item->getID(), + 'groups_id' => 3, + 'type' => Group_Item::GROUP_TYPE_NORMAL, + ], + ); + $DB->insert( + 'glpi_groups_items', + [ + 'itemtype' => $class, + 'items_id' => $item->getID(), + 'groups_id' => 4, + 'type' => Group_Item::GROUP_TYPE_TECH, + ], + ); + $this->boolean($item->getFromDB($item->getID()))->isTrue(); + $this->array($item->fields['groups_id'])->isEqualTo([1, 3]); + $this->array($item->fields['groups_id_tech'])->isEqualTo([2, 4]); + } + + /** + * An empty item should have the groups_id/groups_id_tech fields initialized as an empty array. + * + * @dataProvider itemtypeProvider + */ + public function testGetEmpty(string $class): void + { + $item = new $class(); + $this->boolean($item->getEmpty())->isTrue(); + $this->array($item->fields['groups_id'])->isEmpty(); + $this->array($item->fields['groups_id_tech'])->isEmpty(); + } + + /** + * Check that adding and updating an item with groups_id/groups_id_tech as an integer still works (minor BC, mainly for API scripts). + * + * @dataProvider itemtypeProvider + */ + public function testAddUpdateWithIntGroups(string $class): void + { + $this->login(); // login to bypass some rights checks (e.g. on domain records) + + $input = $this->getMinimalCreationInput($class); + + $item = $this->createItem( + $class, + $input + [ + $class::getNameField() => __FUNCTION__, + 'groups_id' => 1, + 'groups_id_tech' => 2, + ], + ['groups_id', 'groups_id_tech'] // ignore the fields as it will be transformed to an array + ); + $this->array($item->fields['groups_id'])->isEqualTo([1]); + $this->array($item->fields['groups_id_tech'])->isEqualTo([2]); + + $updated = $item->update(['id' => $item->getID(), 'groups_id' => 3, 'groups_id_tech' => 4]); + $this->boolean($updated)->isTrue(); + $this->array($item->fields['groups_id'])->isEqualTo([3]); + $this->array($item->fields['groups_id_tech'])->isEqualTo([4]); + } +} diff --git a/tests/units/RuleAsset.php b/tests/units/RuleAsset.php index 2eff6fe4059..e9e935bfd14 100644 --- a/tests/units/RuleAsset.php +++ b/tests/units/RuleAsset.php @@ -418,7 +418,7 @@ public function testGroupUserAssignFromDefaultUser() ]); $this->integer($computers_id)->isGreaterThan(0); $this->boolean($computer->getFromDB($computers_id))->isTrue(); - $this->integer($computer->getField('groups_id'))->isEqualTo($user->getField('groups_id')); + $this->array($computer->getField('groups_id'))->isEqualTo([$user->getField('groups_id')]); } public function testFirstGroupUserAssignFromUser() @@ -500,6 +500,6 @@ public function testFirstGroupUserAssignFromUser() ]); $this->integer($computers_id)->isGreaterThan(0); $this->boolean($computer->getFromDB($computers_id))->isTrue(); - $this->integer($computer->getField('groups_id'))->isEqualTo($group_id); + $this->array($computer->getField('groups_id'))->isEqualTo([$group_id]); } }