diff --git a/inc/define.php b/inc/define.php index c963820c8c0..8282a1ca421 100644 --- a/inc/define.php +++ b/inc/define.php @@ -338,6 +338,7 @@ $CFG_GLPI['itemdevices'] = []; foreach ($CFG_GLPI['device_types'] as $dtype) { $CFG_GLPI['location_types'][] = 'Item_' . $dtype; + $CFG_GLPI['state_types'][] = 'Item_' . $dtype; $CFG_GLPI["itemdevices"][] = 'Item_' . $dtype; } diff --git a/inc/relation.constant.php b/inc/relation.constant.php index 62c0a6fefcb..46b25e2c754 100644 --- a/inc/relation.constant.php +++ b/inc/relation.constant.php @@ -1400,10 +1400,9 @@ 'glpi_computers' => 'states_id', 'glpi_contracts' => 'states_id', 'glpi_databaseinstances' => 'states_id', - 'glpi_devicegenerics' => 'states_id', - 'glpi_devicesensors' => 'states_id', 'glpi_enclosures' => 'states_id', 'glpi_items_devicebatteries' => 'states_id', + 'glpi_items_devicecameras' => 'states_id', 'glpi_items_devicecases' => 'states_id', 'glpi_items_devicecontrols' => 'states_id', 'glpi_items_devicedrives' => 'states_id', diff --git a/install/migrations/update_10.0.x_to_11.0.0/states.php b/install/migrations/update_10.0.x_to_11.0.0/states.php new file mode 100644 index 00000000000..ebf15a8c173 --- /dev/null +++ b/install/migrations/update_10.0.x_to_11.0.0/states.php @@ -0,0 +1,113 @@ +. + * + * --------------------------------------------------------------------- + */ + +/** + * @var \DBmysql $DB + * @var \Migration $migration + */ + +$default_charset = DBConnection::getDefaultCharset(); +$default_collation = DBConnection::getDefaultCollation(); +$default_key_sign = DBConnection::getDefaultPrimaryKeySignOption(); + +if (!$DB->tableExists('glpi_dropdownvisibilities')) { + $known_visibilities = [ + 'computer', + 'monitor', + 'networkequipment', + 'peripheral', + 'phone', + 'printer', + 'softwareversion', + 'softwarelicense', + 'line', + 'certificate', + 'rack', + 'passivedcequipment', + 'enclosure', + 'pdu', + 'cluster', + 'contract', + 'appliance', + 'databaseinstance', + 'cable', + 'unmanaged' + ]; + + $query = "CREATE TABLE `glpi_dropdownvisibilities` ( + `id` int {$default_key_sign} NOT NULL AUTO_INCREMENT, + `itemtype` varchar(100) NOT NULL DEFAULT '', + `items_id` int {$default_key_sign} NOT NULL DEFAULT '0', + `visible_itemtype` varchar(100) NOT NULL DEFAULT '', + `is_visible` tinyint NOT NULL DEFAULT '1', + PRIMARY KEY (`id`), + KEY `visible_itemtype` (`visible_itemtype`), + KEY `item` (`itemtype`,`items_id`) + ) ENGINE=InnoDB DEFAULT CHARSET={$default_charset} COLLATE={$default_collation} ROW_FORMAT=DYNAMIC;"; + $DB->doQueryOrDie($query, "10.1.0 add table glpi_dropdownvisibilities"); + + $states = $DB->request('glpi_states'); + foreach ($states as $state) { + $insert_data = [ + 'itemtype' => 'State', + 'items_id' => $state['id'], + ]; + + foreach ($known_visibilities as $known_visibility) { + if (isset($state['is_visible_' . $known_visibility])) { + $insert_data['visible_itemtype'] = $known_visibility; + $insert_data['is_visible'] = $state['is_visible_' . $known_visibility]; + $DB->doQueryOrDie($DB->buildInsert('glpi_dropdownvisibilities', $insert_data)); + } + } + } + + foreach ($known_visibilities as $known_visibility) { + if ($DB->fieldExists('glpi_states', 'is_visible_' . $known_visibility)) { + $migration->dropField('glpi_states', 'is_visible_' . $known_visibility); + } + } +} +$migration->displayWarning( + 'States dropdown in devices items forms are now filtered, and, by default, existing states are not visible.' +); + +// Add missing field +$migration->addField('glpi_items_devicecameras', 'states_id', 'fkey'); +$migration->addKey('glpi_items_devicecameras', 'states_id'); + +// Drop unexpected fields +$migration->dropField('glpi_devicegenerics', 'states_id'); +$migration->dropField('glpi_devicesensors', 'states_id'); diff --git a/install/mysql/glpi-empty.sql b/install/mysql/glpi-empty.sql index 25c957182fc..ab86569c8db 100644 --- a/install/mysql/glpi-empty.sql +++ b/install/mysql/glpi-empty.sql @@ -1842,7 +1842,6 @@ CREATE TABLE `glpi_devicegenerics` ( `entities_id` int unsigned NOT NULL DEFAULT '0', `is_recursive` tinyint NOT NULL DEFAULT '0', `locations_id` int unsigned NOT NULL DEFAULT '0', - `states_id` int unsigned NOT NULL DEFAULT '0', `devicegenericmodels_id` int unsigned DEFAULT NULL, `date_mod` timestamp NULL DEFAULT NULL, `date_creation` timestamp NULL DEFAULT NULL, @@ -1853,7 +1852,6 @@ CREATE TABLE `glpi_devicegenerics` ( KEY `entities_id` (`entities_id`), KEY `is_recursive` (`is_recursive`), KEY `locations_id` (`locations_id`), - KEY `states_id` (`states_id`), KEY `date_mod` (`date_mod`), KEY `date_creation` (`date_creation`), KEY `devicegenericmodels_id` (`devicegenericmodels_id`) @@ -2011,6 +2009,7 @@ CREATE TABLE `glpi_items_devicecameras` ( `entities_id` int unsigned NOT NULL DEFAULT '0', `is_recursive` tinyint NOT NULL DEFAULT '0', `locations_id` int unsigned NOT NULL DEFAULT '0', + `states_id` int unsigned NOT NULL DEFAULT '0', PRIMARY KEY (`id`), KEY `items_id` (`items_id`), KEY `devicecameras_id` (`devicecameras_id`), @@ -2019,6 +2018,7 @@ CREATE TABLE `glpi_items_devicecameras` ( KEY `entities_id` (`entities_id`), KEY `is_recursive` (`is_recursive`), KEY `locations_id` (`locations_id`), + KEY `states_id` (`states_id`), KEY `item` (`itemtype`,`items_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; @@ -2376,7 +2376,6 @@ CREATE TABLE `glpi_devicesensors` ( `entities_id` int unsigned NOT NULL DEFAULT '0', `is_recursive` tinyint NOT NULL DEFAULT '0', `locations_id` int unsigned NOT NULL DEFAULT '0', - `states_id` int unsigned NOT NULL DEFAULT '0', `date_mod` timestamp NULL DEFAULT NULL, `date_creation` timestamp NULL DEFAULT NULL, PRIMARY KEY (`id`), @@ -2386,7 +2385,6 @@ CREATE TABLE `glpi_devicesensors` ( KEY `entities_id` (`entities_id`), KEY `is_recursive` (`is_recursive`), KEY `locations_id` (`locations_id`), - KEY `states_id` (`states_id`), KEY `date_mod` (`date_mod`), KEY `date_creation` (`date_creation`), KEY `devicesensormodels_id` (`devicesensormodels_id`) @@ -6990,26 +6988,6 @@ CREATE TABLE `glpi_states` ( `level` int NOT NULL DEFAULT '0', `ancestors_cache` longtext, `sons_cache` longtext, - `is_visible_computer` tinyint NOT NULL DEFAULT '1', - `is_visible_monitor` tinyint NOT NULL DEFAULT '1', - `is_visible_networkequipment` tinyint NOT NULL DEFAULT '1', - `is_visible_peripheral` tinyint NOT NULL DEFAULT '1', - `is_visible_phone` tinyint NOT NULL DEFAULT '1', - `is_visible_printer` tinyint NOT NULL DEFAULT '1', - `is_visible_softwareversion` tinyint NOT NULL DEFAULT '1', - `is_visible_softwarelicense` tinyint NOT NULL DEFAULT '1', - `is_visible_line` tinyint NOT NULL DEFAULT '1', - `is_visible_certificate` tinyint NOT NULL DEFAULT '1', - `is_visible_rack` tinyint NOT NULL DEFAULT '1', - `is_visible_passivedcequipment` tinyint NOT NULL DEFAULT '1', - `is_visible_enclosure` tinyint NOT NULL DEFAULT '1', - `is_visible_pdu` tinyint NOT NULL DEFAULT '1', - `is_visible_cluster` tinyint NOT NULL DEFAULT '1', - `is_visible_contract` tinyint NOT NULL DEFAULT '1', - `is_visible_appliance` tinyint NOT NULL DEFAULT '1', - `is_visible_databaseinstance` tinyint NOT NULL DEFAULT '1', - `is_visible_cable` tinyint NOT NULL DEFAULT '1', - `is_visible_unmanaged` tinyint NOT NULL DEFAULT '1', `is_helpdesk_visible` tinyint NOT NULL DEFAULT '1', `date_mod` timestamp NULL DEFAULT NULL, `date_creation` timestamp NULL DEFAULT NULL, @@ -7018,32 +6996,23 @@ CREATE TABLE `glpi_states` ( KEY `name` (`name`), KEY `entities_id` (`entities_id`), KEY `is_recursive` (`is_recursive`), - KEY `is_visible_computer` (`is_visible_computer`), - KEY `is_visible_monitor` (`is_visible_monitor`), - KEY `is_visible_networkequipment` (`is_visible_networkequipment`), - KEY `is_visible_peripheral` (`is_visible_peripheral`), - KEY `is_visible_phone` (`is_visible_phone`), - KEY `is_visible_printer` (`is_visible_printer`), - KEY `is_visible_softwareversion` (`is_visible_softwareversion`), - KEY `is_visible_softwarelicense` (`is_visible_softwarelicense`), - KEY `is_visible_line` (`is_visible_line`), - KEY `is_visible_certificate` (`is_visible_certificate`), - KEY `is_visible_rack` (`is_visible_rack`), - KEY `is_visible_passivedcequipment` (`is_visible_passivedcequipment`), - KEY `is_visible_enclosure` (`is_visible_enclosure`), - KEY `is_visible_pdu` (`is_visible_pdu`), - KEY `is_visible_cluster` (`is_visible_cluster`), - KEY `is_visible_contract` (`is_visible_contract`), - KEY `is_visible_appliance` (`is_visible_appliance`), - KEY `is_visible_databaseinstance` (`is_visible_databaseinstance`), - KEY `is_visible_cable` (`is_visible_cable`), - KEY `is_visible_unmanaged` (`is_visible_unmanaged`), KEY `is_helpdesk_visible` (`is_helpdesk_visible`), KEY `date_mod` (`date_mod`), KEY `date_creation` (`date_creation`), KEY `level` (`level`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +DROP TABLE IF EXISTS glpi_dropdownvisibilities; +CREATE TABLE `glpi_dropdownvisibilities` ( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `itemtype` varchar(100) NOT NULL DEFAULT '', + `items_id` int unsigned NOT NULL DEFAULT '0', + `visible_itemtype` varchar(100) NOT NULL DEFAULT '', + `is_visible` tinyint NOT NULL DEFAULT '1', + PRIMARY KEY (`id`), + KEY `visible_itemtype` (`visible_itemtype`), + KEY `item` (`itemtype`,`items_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; ### Dump table glpi_suppliers diff --git a/src/Api/HL/Controller/ComponentController.php b/src/Api/HL/Controller/ComponentController.php index 8f96cef2c59..804580535de 100644 --- a/src/Api/HL/Controller/ComponentController.php +++ b/src/Api/HL/Controller/ComponentController.php @@ -241,7 +241,6 @@ protected static function getRawKnownSchemas(): array 'type' => self::getDropdownTypeSchema(class: \DeviceGenericType::class, full_schema: 'GenericDeviceType'), 'model' => self::getDropdownTypeSchema(class: \DeviceGenericModel::class, full_schema: 'GenericDeviceModel'), 'location' => self::getDropdownTypeSchema(class: \Location::class, full_schema: 'Location'), - 'state' => self::getDropdownTypeSchema(class: \State::class, full_schema: 'State'), ] ], 'GraphicCardModel' => [ @@ -373,7 +372,6 @@ protected static function getRawKnownSchemas(): array 'type' => self::getDropdownTypeSchema(class: \DeviceSensorType::class, full_schema: 'SensorType'), 'model' => self::getDropdownTypeSchema(class: \DeviceSensorModel::class, full_schema: 'SensorModel'), 'location' => self::getDropdownTypeSchema(class: \Location::class, full_schema: 'Location'), - 'state' => self::getDropdownTypeSchema(class: \State::class, full_schema: 'State'), ] ], 'SIMCardType' => [ diff --git a/src/Api/HL/Controller/DropdownController.php b/src/Api/HL/Controller/DropdownController.php index fb2039a2365..ad39c74c390 100644 --- a/src/Api/HL/Controller/DropdownController.php +++ b/src/Api/HL/Controller/DropdownController.php @@ -76,6 +76,9 @@ final class DropdownController extends AbstractController protected static function getRawKnownSchemas(): array { + /** @var array $CFG_GLPI */ + global $CFG_GLPI; + $schemas = []; $schemas['Location'] = [ @@ -129,30 +132,39 @@ protected static function getRawKnownSchemas(): array 'is_recursive' => ['type' => Doc\Schema::TYPE_BOOLEAN], 'parent' => self::getDropdownTypeSchema(class: State::class, full_schema: 'State'), 'level' => ['type' => Doc\Schema::TYPE_INTEGER], - 'is_visible_computer' => ['type' => Doc\Schema::TYPE_BOOLEAN], - 'is_visible_monitor' => ['type' => Doc\Schema::TYPE_BOOLEAN], - 'is_visible_network_equipment' => ['x-field' => 'is_visible_networkequipment','type' => Doc\Schema::TYPE_BOOLEAN], - 'is_visible_peripheral' => ['type' => Doc\Schema::TYPE_BOOLEAN], - 'is_visible_phone' => ['type' => Doc\Schema::TYPE_BOOLEAN], - 'is_visible_printer' => ['type' => Doc\Schema::TYPE_BOOLEAN], - 'is_visible_software_version' => ['x-field' => 'is_visible_softwareversion','type' => Doc\Schema::TYPE_BOOLEAN], - 'is_visible_software_license' => ['x-field' => 'is_visible_softwarelicense','type' => Doc\Schema::TYPE_BOOLEAN], - 'is_visible_line' => ['type' => Doc\Schema::TYPE_BOOLEAN], - 'is_visible_certificate' => ['type' => Doc\Schema::TYPE_BOOLEAN], - 'is_visible_rack' => ['type' => Doc\Schema::TYPE_BOOLEAN], - 'is_visible_passive_dcequipment' => ['x-field' => 'is_visible_passivedcequipment', 'type' => Doc\Schema::TYPE_BOOLEAN], - 'is_visible_enclosure' => ['type' => Doc\Schema::TYPE_BOOLEAN], - 'is_visible_pdu' => ['type' => Doc\Schema::TYPE_BOOLEAN], - 'is_visible_cluster' => ['type' => Doc\Schema::TYPE_BOOLEAN], - 'is_visible_contract' => ['type' => Doc\Schema::TYPE_BOOLEAN], - 'is_visible_appliance' => ['type' => Doc\Schema::TYPE_BOOLEAN], - 'is_visible_cable' => ['type' => Doc\Schema::TYPE_BOOLEAN], - 'is_visible_database_instance' => ['x-field' => 'is_visible_databaseinstance', 'type' => Doc\Schema::TYPE_BOOLEAN], 'is_visible_helpdesk' => ['x-field' => 'is_helpdesk_visible', '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], ] ]; + $visiblities = array_map('strtolower', $CFG_GLPI['state_types']); + + $schemas['State_Visibilities'] = [ + 'type' => Doc\Schema::TYPE_OBJECT, + 'properties' => [] + ]; + $schemas['State']['properties']['visibilities'] = [ + 'type' => Doc\Schema::TYPE_OBJECT, + 'x-full-schema' => 'State_Visibilities', + ]; + + foreach ($visiblities as $visiblity) { + $schemas['State_Visibilities']['properties'][$visiblity] = [ + 'type' => Doc\Schema::TYPE_BOOLEAN, + 'x-field' => 'is_visible', + 'x-readonly' => true, + 'x-join' => [ + 'table' => \DropdownVisibility::getTable(), + 'fkey' => 'id', + 'field' => 'items_id', + 'condition' => [ + 'itemtype' => 'State', + 'visible_itemtype' => $visiblity + ] + ] + ]; + } + $schemas['State']['properties']['visibilities']['properties'] = $schemas['State_Visibilities']['properties']; $schemas['Manufacturer'] = [ 'type' => Doc\Schema::TYPE_OBJECT, diff --git a/src/Api/HL/Controller/ReportController.php b/src/Api/HL/Controller/ReportController.php index b850861a49e..dbe3d2d7ab1 100644 --- a/src/Api/HL/Controller/ReportController.php +++ b/src/Api/HL/Controller/ReportController.php @@ -241,18 +241,8 @@ protected static function getRawKnownSchemas(): array 'name' => [ 'type' => Doc\Schema::TYPE_STRING, ], - 'entity' => [ - 'type' => Doc\Schema::TYPE_OBJECT, + 'entity' => self::getDropdownTypeSchema(class: \Entity::class, full_schema: 'Entity') + [ 'description' => 'The entity the item belongs to', - 'properties' => [ - 'id' => [ - 'type' => Doc\Schema::TYPE_INTEGER, - 'format' => Doc\Schema::FORMAT_INTEGER_INT64, - ], - 'name' => [ - 'type' => Doc\Schema::TYPE_STRING, - ], - ] ], 'is_deleted' => [ 'type' => Doc\Schema::TYPE_BOOLEAN, diff --git a/src/Api/HL/Doc/Schema.php b/src/Api/HL/Doc/Schema.php index 9bb804b4c1c..164d694b119 100644 --- a/src/Api/HL/Doc/Schema.php +++ b/src/Api/HL/Doc/Schema.php @@ -265,6 +265,24 @@ public static function getJoins(array $props, string $prefix = '', ?array $paren } } } + if ($prefix === '') { + // Fix parent_join for all joins + foreach ($joins as $join_name => $join) { + if (isset($join['join_parent'])) { + // This join is supposed to have a parent + // The set parent may not be correct currently. The join's parent may in fact be an ancestor of the one set + // We need to check if the current parent exists in the list of joins. If not, we need to find the correct parent by walking up the tree + $parent = $join['join_parent']; + while ($parent !== '') { + if (isset($joins[$parent])) { + $joins[$join_name]['join_parent'] = $parent; + break; + } + $parent = substr($parent, 0, strrpos($parent, chr(0x1F))); + } + } + } + } return $joins; } diff --git a/src/Api/HL/GraphQL.php b/src/Api/HL/GraphQL.php index 4e62ad9bc29..3e76e75f1a3 100644 --- a/src/Api/HL/GraphQL.php +++ b/src/Api/HL/GraphQL.php @@ -84,6 +84,7 @@ public static function processRequest(Request $request): array } ); } catch (\Throwable $e) { + trigger_error("Error processing GraphQL request: {$e->getMessage()}", E_USER_WARNING); return []; } return $result->toArray(); diff --git a/src/Api/HL/GraphQLGenerator.php b/src/Api/HL/GraphQLGenerator.php index cd18d7e17fb..ec957bb2a72 100644 --- a/src/Api/HL/GraphQLGenerator.php +++ b/src/Api/HL/GraphQLGenerator.php @@ -93,9 +93,6 @@ private function loadTypes() { $component_schemas = OpenAPIGenerator::getComponentSchemas(); foreach ($component_schemas as $schema_name => $schema) { - if (!isset($schema['x-itemtype'])) { - continue; - } $new_types = $this->getTypesForSchema($schema_name, $schema); foreach ($new_types as $type_name => $type) { $this->types[$type_name] = $type; diff --git a/src/Api/HL/Search.php b/src/Api/HL/Search.php index fa0b5296cf7..ed3f2431107 100644 --- a/src/Api/HL/Search.php +++ b/src/Api/HL/Search.php @@ -200,8 +200,7 @@ private function getSelectCriteriaForProperty(string $prop_name, bool $distinct_ $sql_field = $this->getSQLFieldForProperty($prop_name); $expression = $this->db_read::quoteName($sql_field); if (str_contains($sql_field, '.')) { - $join_name = substr($sql_field, 0, strrpos($sql_field, '.')); - $join_name = str_replace(chr(0x1F), '.', $join_name); + $join_name = $this->getJoinNameForProperty($prop_name); // Check if the join property is in an array. If so, we need to concat each result. if (array_key_exists($join_name, $this->joins)) { $join_def = $this->joins[$join_name]; @@ -577,6 +576,17 @@ static function ($prop_name) use ($primary_key, $join) { throw new RuntimeException("Cannot find primary key property for join $join"); } + private function getJoinNameForProperty(string $prop_name): string + { + if (array_key_exists(str_replace(chr(0x1F), '.', $prop_name), $this->joins)) { + $join_name = str_replace(chr(0x1F), '.', $prop_name); + } else { + $join_name = substr($prop_name, 0, strrpos($prop_name, chr(0x1F))); + $join_name = str_replace(chr(0x1F), '.', $join_name); + } + return $join_name; + } + /** * @return array Matching records in the format Itemtype => IDs * @phpstan-return array @@ -796,8 +806,10 @@ private function assembleHydratedRecords(array $dehydrated_row, string $schema_n } } else { // Add the joined item fields - $join_name = substr($dehydrated_ref, 0, strrpos($dehydrated_ref, chr(0x1F))); - $join_name = str_replace(chr(0x1F), '.', $join_name); + $join_name = $this->getJoinNameForProperty($dehydrated_ref); + if (isset($this->flattened_properties[$join_name])) { + continue; + } if (!ArrayPathAccessor::hasElementByArrayPath($hydrated_record, $join_name)) { ArrayPathAccessor::setElementByArrayPath($hydrated_record, $join_name, []); } @@ -824,7 +836,9 @@ private function assembleHydratedRecords(array $dehydrated_row, string $schema_n // Do this last as some scalar joined properties may be nested and have other data added after the main record was built foreach ($dehydrated_row as $k => $v) { $normalized_k = str_replace(chr(0x1F), '.', $k); - if (isset($this->joins[$normalized_k]) && !isset($hydrated_record[$normalized_k])) { + if (isset($this->joins[$normalized_k]) && !ArrayPathAccessor::hasElementByArrayPath($hydrated_record, $normalized_k)) { + $v = explode(chr(0x1E), $v); + $v = end($v); ArrayPathAccessor::setElementByArrayPath($hydrated_record, $normalized_k, $v); } } @@ -948,8 +962,7 @@ private function hydrateRecords(array $records): array $criteria['SELECT'][] = new QueryExpression($this->db_read::quoteValue($schema_name), '_itemtype'); } } else { - $join_name = substr($fkey, 0, strrpos($fkey, chr(0x1F))); - $join_name = str_replace(chr(0x1F), '.', $join_name); + $join_name = $this->getJoinNameForProperty($fkey); $props_to_use = array_filter($this->flattened_properties, function ($prop_name) use ($join_name) { if (isset($this->joins[$prop_name])) { /** Scalar joined properties are fetched directly during {@link self::getMatchingRecords()} */ diff --git a/src/Appliance.php b/src/Appliance.php index 68a93a87c77..d9af5c66dcd 100644 --- a/src/Appliance.php +++ b/src/Appliance.php @@ -42,6 +42,7 @@ class Appliance extends CommonDBTM { use Glpi\Features\Clonable; + use Glpi\Features\State; use AssetImage; // From CommonDBTM @@ -291,11 +292,11 @@ public function rawSearchOptions() $tab[] = [ 'id' => '32', - 'table' => 'glpi_states', + 'table' => State::getTable(), 'field' => 'completename', 'name' => __('Status'), 'datatype' => 'dropdown', - 'condition' => ['is_visible_appliance' => 1] + 'condition' => $this->getStateVisibilityCriteria() ]; $tab = array_merge($tab, Certificate::rawSearchOptionsToAdd()); diff --git a/src/Asset/Asset.php b/src/Asset/Asset.php index f6a1e0e7357..737a76f6684 100644 --- a/src/Asset/Asset.php +++ b/src/Asset/Asset.php @@ -38,7 +38,6 @@ use CommonDBTM; use Glpi\Application\View\TemplateRenderer; use Entity; -use Glpi\Features\Clonable; use Group; use Location; use Manufacturer; @@ -48,7 +47,8 @@ abstract class Asset extends CommonDBTM { - use Clonable; + use \Glpi\Features\Clonable; + use \Glpi\Features\State; final public function __construct() { @@ -189,7 +189,7 @@ public function rawSearchOptions() 'field' => 'completename', 'name' => __('Status'), 'datatype' => 'dropdown', - // TODO 'condition' to filter values + 'condition' => $this->getStateVisibilityCriteria(), ]; $search_options[] = [ diff --git a/src/Asset/AssetDefinitionManager.php b/src/Asset/AssetDefinitionManager.php index baf630abc81..1036e516fc0 100644 --- a/src/Asset/AssetDefinitionManager.php +++ b/src/Asset/AssetDefinitionManager.php @@ -178,6 +178,7 @@ private function boostrapConcreteClass(AssetDefinition $definition): void 'linkuser_tech_types', 'linkgroup_tech_types', 'location_types', + 'state_types', 'ticket_types', ]; foreach ($config_keys as $config_key) { diff --git a/src/Cable.php b/src/Cable.php index 414b5c046d8..8a28130dce8 100644 --- a/src/Cable.php +++ b/src/Cable.php @@ -41,6 +41,7 @@ class Cable extends CommonDBTM { use Glpi\Features\Clonable; + use Glpi\Features\State; // From CommonDBTM public $dohistory = true; @@ -299,11 +300,11 @@ public function rawSearchOptions() $tab[] = [ 'id' => '31', - 'table' => 'glpi_states', + 'table' => State::getTable(), 'field' => 'completename', 'name' => __('Status'), 'datatype' => 'dropdown', - 'condition' => ['is_visible_cable' => 1] + 'condition' => $this->getStateVisibilityCriteria() ]; $tab[] = [ diff --git a/src/Certificate.php b/src/Certificate.php index cf217c2e57d..8767db8e4a5 100644 --- a/src/Certificate.php +++ b/src/Certificate.php @@ -49,6 +49,7 @@ class Certificate extends CommonDBTM { use Glpi\Features\Clonable; + use Glpi\Features\State; public $dohistory = true; public static $rightname = "certificate"; @@ -256,11 +257,11 @@ public function rawSearchOptions() $tab[] = [ 'id' => '31', - 'table' => 'glpi_states', + 'table' => State::getTable(), 'field' => 'completename', 'name' => __('Status'), 'datatype' => 'dropdown', - 'condition' => ['is_visible_certificate' => 1] + 'condition' => $this->getStateVisibilityCriteria() ]; $tab[] = [ diff --git a/src/Cluster.php b/src/Cluster.php index 43af71bab5c..a8f5f176966 100644 --- a/src/Cluster.php +++ b/src/Cluster.php @@ -39,6 +39,7 @@ class Cluster extends CommonDBTM { use Glpi\Features\Clonable; + use Glpi\Features\State; // From CommonDBTM public $dohistory = true; @@ -90,11 +91,11 @@ public function rawSearchOptions() $tab[] = [ 'id' => '31', - 'table' => 'glpi_states', + 'table' => State::getTable(), 'field' => 'completename', 'name' => __('Status'), 'datatype' => 'dropdown', - 'condition' => ['is_visible_cluster' => 1] + 'condition' => $this->getStateVisibilityCriteria() ]; $tab[] = [ diff --git a/src/Computer.php b/src/Computer.php index f84a9c43b56..552a1e65bc1 100644 --- a/src/Computer.php +++ b/src/Computer.php @@ -43,6 +43,7 @@ class Computer extends CommonDBTM use Glpi\Features\DCBreadcrumb; use Glpi\Features\Clonable; use Glpi\Features\Inventoriable; + use Glpi\Features\State; // From CommonDBTM public $dohistory = true; @@ -195,7 +196,7 @@ public function post_updateItem($history = true) ) { $changes['states_id'] = $input['states_id']; } - // Update loction of attached items + // Update location of attached items if ( $this->updates[$i] == 'locations_id' && Entity::getUsedConfig('is_location_autoupdate', $this->getEntityID()) @@ -420,11 +421,11 @@ public function rawSearchOptions() $tab[] = [ 'id' => '31', - 'table' => 'glpi_states', + 'table' => State::getTable(), 'field' => 'completename', 'name' => __('Status'), 'datatype' => 'dropdown', - 'condition' => ['is_visible_computer' => 1] + 'condition' => $this->getStateVisibilityCriteria(), ]; $tab[] = [ diff --git a/src/Contract.php b/src/Contract.php index 9a7684bb0d9..a01dfbf1bc0 100644 --- a/src/Contract.php +++ b/src/Contract.php @@ -43,6 +43,7 @@ class Contract extends CommonDBTM { use Glpi\Features\Clonable; + use Glpi\Features\State; // From CommonDBTM public $dohistory = true; @@ -513,11 +514,11 @@ public function rawSearchOptions() $tab[] = [ 'id' => '31', - 'table' => 'glpi_states', + 'table' => State::getTable(), 'field' => 'completename', 'name' => __('Status'), 'datatype' => 'dropdown', - 'condition' => ['is_visible_contract' => 1] + 'condition' => $this->getStateVisibilityCriteria() ]; $tab[] = [ diff --git a/src/DBmysqlIterator.php b/src/DBmysqlIterator.php index 19380971f4a..70d437ce4fd 100644 --- a/src/DBmysqlIterator.php +++ b/src/DBmysqlIterator.php @@ -725,8 +725,21 @@ private function analyseFkey($values) (is_numeric($t2) ? DBmysql::quoteName($f2) : DBmysql::quoteName($t2) . '.' . DBmysql::quoteName($f2)); } } else if (count($values) == 3) { - $condition = array_pop($values); - $fkey = $this->analyseFkey($values); + $real_values = []; + foreach ($values as $k => $v) { + if (is_array($v)) { + $condition = $v; + } else { + $real_values[$k] = $v; + } + } + + if (!isset($condition)) { + //in theory, should never happen + $condition = array_pop($real_values); + } + + $fkey = $this->analyseFkey($real_values); $condition_value = $this->analyseCrit(current($condition)); if (!empty(trim($condition_value))) { return $fkey . ' ' . key($condition) . ' ' . $condition_value; diff --git a/src/DatabaseInstance.php b/src/DatabaseInstance.php index b8f8255f7d0..529828a6e39 100644 --- a/src/DatabaseInstance.php +++ b/src/DatabaseInstance.php @@ -39,6 +39,7 @@ class DatabaseInstance extends CommonDBTM { use Glpi\Features\Clonable; use Glpi\Features\Inventoriable; + use Glpi\Features\State; // From CommonDBTM public $dohistory = true; @@ -205,9 +206,10 @@ public function rawSearchOptions() $tab[] = [ 'id' => '41', 'table' => State::getTable(), - 'field' => 'name', - 'name' => _n('State', 'States', 1), - 'datatype' => 'dropdown' + 'field' => 'completename', + 'name' => __('Status'), + 'datatype' => 'dropdown', + 'condition' => $this->getStateVisibilityCriteria(), ]; $tab[] = [ diff --git a/src/Dropdown.php b/src/Dropdown.php index 02d33d14afe..6c2a9ef0120 100644 --- a/src/Dropdown.php +++ b/src/Dropdown.php @@ -2659,7 +2659,7 @@ public static function getDropdownValue($post, $json = true) */ global $CFG_GLPI, $DB; - // check if asked itemtype is the one originaly requested by the form + // check if asked itemtype is the one originally requested by the form if (!Session::validateIDOR($post)) { return; } diff --git a/src/DropdownVisibility.php b/src/DropdownVisibility.php new file mode 100644 index 00000000000..7fccf0ea0fa --- /dev/null +++ b/src/DropdownVisibility.php @@ -0,0 +1,42 @@ +. + * + * --------------------------------------------------------------------- + */ + +class DropdownVisibility extends CommonDBChild +{ + public static $itemtype = 'itemtype'; + public static $items_id = 'items_id'; + public $dohistory = false; + public static $logs_for_parent = false; +} diff --git a/src/Enclosure.php b/src/Enclosure.php index 818e249aae4..42bb2d82b84 100644 --- a/src/Enclosure.php +++ b/src/Enclosure.php @@ -42,6 +42,7 @@ class Enclosure extends CommonDBTM { use Glpi\Features\DCBreadcrumb; use Glpi\Features\Clonable; + use Glpi\Features\State; // From CommonDBTM public $dohistory = true; @@ -126,11 +127,11 @@ public function rawSearchOptions() $tab[] = [ 'id' => '31', - 'table' => 'glpi_states', + 'table' => State::getTable(), 'field' => 'completename', 'name' => __('Status'), 'datatype' => 'dropdown', - 'condition' => ['is_visible_enclosure' => 1] + 'condition' => $this->getStateVisibilityCriteria() ]; $tab[] = [ diff --git a/src/Features/State.php b/src/Features/State.php new file mode 100644 index 00000000000..ede86e18a26 --- /dev/null +++ b/src/Features/State.php @@ -0,0 +1,110 @@ +. + * + * --------------------------------------------------------------------- + */ + +namespace Glpi\Features; + +use DropdownVisibility; + +trait State +{ + /** + * Check if itemtype class is present in configuration array + * + * @return void + */ + private function checkSetup(): void + { + /** @var array $CFG_GLPI */ + global $CFG_GLPI; + + if (!in_array(static::class, $CFG_GLPI['state_types'])) { + trigger_error( + sprintf( + 'Class %s must be present in $CFG_GLPI[\'state_types\']', + static::class + ), + E_USER_ERROR + ); + } + } + + /** + * Get the visibility of the state field for the item + * + * @param int $id State ID + * + * @return bool + */ + public function isStateVisible(int $id): bool + { + $this->checkSetup(); + $dropdownVisibility = new DropdownVisibility(); + return $dropdownVisibility->getFromDBByCrit([ + 'itemtype' => \State::getType(), + 'items_id' => $id, + 'visible_itemtype' => static::class, + 'is_visible' => 1 + ]); + } + + /** + * Get the visibility criteria of the state field to use a filter condition + * + * @return array + */ + public function getStateVisibilityCriteria(): array + { + $this->checkSetup(); + return [ + 'LEFT JOIN' => [ + DropdownVisibility::getTable() => [ + 'ON' => [ + DropdownVisibility::getTable() => 'items_id', + \State::getTable() => 'id', [ + 'AND' => [ + DropdownVisibility::getTable() . '.itemtype' => \State::getType() + ] + ] + ] + ] + ], + 'WHERE' => [ + DropdownVisibility::getTable() . '.itemtype' => \State::getType(), + DropdownVisibility::getTable() . '.visible_itemtype' => static::class, + DropdownVisibility::getTable() . '.is_visible' => 1 + ] + ]; + } +} diff --git a/src/Inventory/Conf.php b/src/Inventory/Conf.php index 6e5b9370a84..f4b388fe74c 100644 --- a/src/Inventory/Conf.php +++ b/src/Inventory/Conf.php @@ -952,10 +952,14 @@ function changestatus() { echo ""; $condition = []; foreach ($CFG_GLPI['inventory_types'] as $inv_type) { - $condition['is_visible_' . strtolower($inv_type)] = 1; + if ( + Toolbox::hasTrait($inv_type, \Glpi\Features\State::class) + && $inv_item = getItemForItemtype($inv_type) + ) { + $condition[] = $inv_item->getStateVisibilityCriteria(); + } } - State::dropdown( [ 'name' => 'stale_agents_status_condition[]', diff --git a/src/Item_Devices.php b/src/Item_Devices.php index f4949bb3f06..b8df30c4e73 100644 --- a/src/Item_Devices.php +++ b/src/Item_Devices.php @@ -46,6 +46,8 @@ **/ class Item_Devices extends CommonDBRelation { + use Glpi\Features\State; + public static $itemtype_1 = 'itemtype'; public static $items_id_1 = 'items_id'; public static $mustBeAttached_1 = false; diff --git a/src/Line.php b/src/Line.php index 57d75c68409..4f87acf893a 100644 --- a/src/Line.php +++ b/src/Line.php @@ -42,7 +42,9 @@ class Line extends CommonDBTM { - // From CommonDBTM + use Glpi\Features\State; + + // From CommonDBTM public $dohistory = true; public static $rightname = 'line'; @@ -146,11 +148,11 @@ public function rawSearchOptions() $tab[] = [ 'id' => '31', - 'table' => 'glpi_states', + 'table' => State::getTable(), 'field' => 'completename', 'name' => __('Status'), 'datatype' => 'dropdown', - 'condition' => ['is_visible_line' => 1] + 'condition' => $this->getStateVisibilityCriteria() ]; $tab[] = [ diff --git a/src/Monitor.php b/src/Monitor.php index 204445f01ba..ea0d7286bcf 100644 --- a/src/Monitor.php +++ b/src/Monitor.php @@ -43,6 +43,7 @@ class Monitor extends CommonDBTM use Glpi\Features\DCBreadcrumb; use Glpi\Features\Clonable; use Glpi\Features\Inventoriable; + use Glpi\Features\State; // From CommonDBTM public $dohistory = true; @@ -233,11 +234,11 @@ public function rawSearchOptions() $tab[] = [ 'id' => '31', - 'table' => 'glpi_states', + 'table' => State::getTable(), 'field' => 'completename', 'name' => __('Status'), 'datatype' => 'dropdown', - 'condition' => ['is_visible_monitor' => 1] + 'condition' => $this->getStateVisibilityCriteria() ]; $tab[] = [ diff --git a/src/NetworkEquipment.php b/src/NetworkEquipment.php index d17097c4d07..c582ad99eb1 100644 --- a/src/NetworkEquipment.php +++ b/src/NetworkEquipment.php @@ -45,6 +45,7 @@ class NetworkEquipment extends CommonDBTM use Glpi\Features\DCBreadcrumb; use Glpi\Features\Clonable; use Glpi\Features\Inventoriable; + use Glpi\Features\State; // From CommonDBTM public $dohistory = true; @@ -297,11 +298,11 @@ public function rawSearchOptions() $tab[] = [ 'id' => '31', - 'table' => 'glpi_states', + 'table' => State::getTable(), 'field' => 'completename', 'name' => __('Status'), 'datatype' => 'dropdown', - 'condition' => ['is_visible_networkequipment' => 1] + 'condition' => $this->getStateVisibilityCriteria() ]; $tab[] = [ diff --git a/src/PDU.php b/src/PDU.php index e145f706b1d..9b83e2f79fe 100644 --- a/src/PDU.php +++ b/src/PDU.php @@ -40,6 +40,7 @@ class PDU extends CommonDBTM { use Glpi\Features\DCBreadcrumb; use Glpi\Features\Clonable; + use Glpi\Features\State; // From CommonDBTM public $dohistory = true; @@ -145,11 +146,11 @@ public function rawSearchOptions() $tab[] = [ 'id' => '31', - 'table' => 'glpi_states', + 'table' => State::getTable(), 'field' => 'completename', 'name' => __('Status'), 'datatype' => 'dropdown', - 'condition' => ['is_visible_pdu' => 1] + 'condition' => $this->getStateVisibilityCriteria() ]; $tab[] = [ diff --git a/src/PassiveDCEquipment.php b/src/PassiveDCEquipment.php index ec69079c023..017cad90865 100644 --- a/src/PassiveDCEquipment.php +++ b/src/PassiveDCEquipment.php @@ -43,6 +43,7 @@ class PassiveDCEquipment extends CommonDBTM { use Clonable; use Glpi\Features\DCBreadcrumb; + use Glpi\Features\State; // From CommonDBTM public $dohistory = true; @@ -142,11 +143,11 @@ public function rawSearchOptions() $tab[] = [ 'id' => '31', - 'table' => 'glpi_states', + 'table' => State::getTable(), 'field' => 'completename', 'name' => __('Status'), 'datatype' => 'dropdown', - 'condition' => ['is_visible_passivedcequipment' => 1] + 'condition' => $this->getStateVisibilityCriteria() ]; $tab[] = [ diff --git a/src/Peripheral.php b/src/Peripheral.php index 5d5c3c08177..e561273e469 100644 --- a/src/Peripheral.php +++ b/src/Peripheral.php @@ -43,6 +43,7 @@ class Peripheral extends CommonDBTM use Glpi\Features\DCBreadcrumb; use Glpi\Features\Clonable; use Glpi\Features\Inventoriable; + use Glpi\Features\State; // From CommonDBTM public $dohistory = true; @@ -218,11 +219,11 @@ public function rawSearchOptions() $tab[] = [ 'id' => '31', - 'table' => 'glpi_states', + 'table' => State::getTable(), 'field' => 'completename', 'name' => __('Status'), 'datatype' => 'dropdown', - 'condition' => ['is_visible_peripheral' => 1] + 'condition' => $this->getStateVisibilityCriteria() ]; $tab[] = [ diff --git a/src/Phone.php b/src/Phone.php index 4ac80b57132..2e912bac9dc 100644 --- a/src/Phone.php +++ b/src/Phone.php @@ -43,6 +43,7 @@ class Phone extends CommonDBTM { use Glpi\Features\Clonable; use Glpi\Features\Inventoriable; + use Glpi\Features\State; // From CommonDBTM public $dohistory = true; @@ -256,11 +257,11 @@ public function rawSearchOptions() $tab[] = [ 'id' => '31', - 'table' => 'glpi_states', + 'table' => State::getTable(), 'field' => 'completename', 'name' => __('Status'), 'datatype' => 'dropdown', - 'condition' => ['is_visible_phone' => 1] + 'condition' => $this->getStateVisibilityCriteria() ]; $tab[] = [ diff --git a/src/Printer.php b/src/Printer.php index b1c5511136a..efd14629a37 100644 --- a/src/Printer.php +++ b/src/Printer.php @@ -45,6 +45,7 @@ class Printer extends CommonDBTM { use Glpi\Features\Clonable; use Glpi\Features\Inventoriable; + use Glpi\Features\State; // From CommonDBTM public $dohistory = true; @@ -380,11 +381,11 @@ public function rawSearchOptions() $tab[] = [ 'id' => '31', - 'table' => 'glpi_states', + 'table' => State::getTable(), 'field' => 'completename', 'name' => __('Status'), 'datatype' => 'dropdown', - 'condition' => ['is_visible_printer' => 1] + 'condition' => $this->getStateVisibilityCriteria() ]; $tab[] = [ diff --git a/src/Rack.php b/src/Rack.php index 4be46541d75..152c46e4b3e 100644 --- a/src/Rack.php +++ b/src/Rack.php @@ -41,6 +41,7 @@ class Rack extends CommonDBTM { use Glpi\Features\DCBreadcrumb; + use Glpi\Features\State; const FRONT = 0; const REAR = 1; @@ -177,11 +178,11 @@ public function rawSearchOptions() $tab[] = [ 'id' => '31', - 'table' => 'glpi_states', + 'table' => State::getTable(), 'field' => 'completename', 'name' => __('Status'), 'datatype' => 'dropdown', - 'condition' => ['is_visible_rack' => 1] + 'condition' => $this->getStateVisibilityCriteria() ]; $tab[] = [ diff --git a/src/SoftwareLicense.php b/src/SoftwareLicense.php index b58283b64d8..a03fce9e6e0 100644 --- a/src/SoftwareLicense.php +++ b/src/SoftwareLicense.php @@ -44,6 +44,7 @@ class SoftwareLicense extends CommonTreeDropdown { use Glpi\Features\Clonable; + use Glpi\Features\State; use AssetImage; /// TODO move to CommonDBChild ? @@ -499,11 +500,11 @@ public function rawSearchOptions() $tab[] = [ 'id' => '31', - 'table' => 'glpi_states', + 'table' => State::getTable(), 'field' => 'completename', 'name' => __('Status'), 'datatype' => 'dropdown', - 'condition' => ['is_visible_softwarelicense' => 1] + 'condition' => $this->getStateVisibilityCriteria() ]; $tab[] = [ diff --git a/src/SoftwareVersion.php b/src/SoftwareVersion.php index 9924ab7e9a7..e5c76fbd2a3 100644 --- a/src/SoftwareVersion.php +++ b/src/SoftwareVersion.php @@ -38,6 +38,8 @@ **/ class SoftwareVersion extends CommonDBChild { + use Glpi\Features\State; + // From CommonDBTM public $dohistory = true; @@ -146,7 +148,7 @@ public function showForm($ID, array $options = []) echo "" . __('Status') . ""; State::dropdown(['value' => $this->fields["states_id"], 'entity' => $this->fields["entities_id"], - 'condition' => ['is_visible_softwareversion' => 1] + 'condition' => self::getStateVisibilityCriteria() ]); echo "\n"; @@ -199,11 +201,11 @@ public function rawSearchOptions() $tab[] = [ 'id' => '31', - 'table' => 'glpi_states', + 'table' => State::getTable(), 'field' => 'completename', 'name' => __('Status'), 'datatype' => 'dropdown', - 'condition' => ['is_visible_softwareversion' => 1] + 'condition' => self::getStateVisibilityCriteria() ]; $tab[] = [ @@ -259,10 +261,10 @@ public static function dropdownForOneSoftware($options = []) 'DISTINCT' => true, 'FROM' => 'glpi_softwareversions', 'LEFT JOIN' => [ - 'glpi_states' => [ + State::getTable() => [ 'ON' => [ 'glpi_softwareversions' => 'states_id', - 'glpi_states' => 'id' + State::getTable() => 'id' ] ] ], @@ -330,10 +332,10 @@ public static function showForSoftware(Software $soft) ], 'FROM' => 'glpi_softwareversions', 'LEFT JOIN' => [ - 'glpi_states' => [ + State::getTable() => [ 'ON' => [ 'glpi_softwareversions' => 'states_id', - 'glpi_states' => 'id' + State::getTable() => 'id' ] ] ], diff --git a/src/State.php b/src/State.php index 14912d4ea7b..306d2a85866 100644 --- a/src/State.php +++ b/src/State.php @@ -302,11 +302,6 @@ public function cleanDBonPurge() } - /** - * @since 0.85 - * - * @see CommonTreeDropdown::prepareInputForAdd() - **/ public function prepareInputForAdd($input) { if (!isset($input['states_id'])) { @@ -324,7 +319,7 @@ public function prepareInputForAdd($input) $input = parent::prepareInputForAdd($input); $state = new self(); - // Get visibility information from parent if not set + // Get visibility information from parent if not set if (isset($input['states_id']) && $state->getFromDB($input['states_id'])) { foreach ($this->getvisibilityFields() as $type => $field) { if (!isset($input[$field]) && isset($state->fields[$field])) { @@ -335,6 +330,46 @@ public function prepareInputForAdd($input) return $input; } + public function post_addItem() + { + $state_visibility = new DropdownVisibility(); + foreach ($this->getvisibilityFields() as $itemtype => $field) { + if (isset($this->input[$field])) { + $state_visibility->add([ + 'itemtype' => State::getType(), + 'items_id' => $this->fields['id'], + 'visible_itemtype' => $itemtype, + 'is_visible' => $this->input[$field] + ]); + } + } + + parent::post_addItem(); + } + + public function post_updateItem($history = true) + { + $state_visibility = new DropdownVisibility(); + foreach ($this->getvisibilityFields() as $itemtype => $field) { + if (isset($this->input[$field])) { + if ($state_visibility->getFromDBByCrit(['itemtype' => self::getType(), 'items_id' => $this->input['id'], 'visible_itemtype' => $itemtype])) { + $state_visibility->update([ + 'id' => $state_visibility->fields['id'], + 'is_visible' => $this->input[$field] + ]); + } else { + $state_visibility->add([ + 'itemtype' => State::getType(), + 'items_id' => $this->fields['id'], + 'visible_itemtype' => $itemtype, + 'is_visible' => $this->input[$field] + ]); + } + } + } + + parent::post_updateItem(); + } public function rawSearchOptions() { @@ -342,210 +377,362 @@ public function rawSearchOptions() $tab[] = [ 'id' => '21', - 'table' => $this->getTable(), - 'field' => 'is_visible_computer', + 'table' => DropdownVisibility::getTable(), + 'field' => 'is_visible', 'name' => sprintf(__('%1$s - %2$s'), __('Visibility'), Computer::getTypeName(Session::getPluralNumber())), - 'datatype' => 'bool' + 'datatype' => 'bool', + 'joinparams' => [ + 'jointype' => 'itemtypeonly', + 'table' => $this->getTable(), + 'condition' => [ + 'NEWTABLE.visible_itemtype' => 'Computer', + 'NEWTABLE.items_id' => new QueryExpression('REFTABLE.id') + ] + ] ]; $tab[] = [ 'id' => '22', - 'table' => $this->getTable(), - 'field' => 'is_visible_softwareversion', + 'table' => DropdownVisibility::getTable(), + 'field' => 'is_visible', 'name' => sprintf( __('%1$s - %2$s'), __('Visibility'), SoftwareVersion::getTypeName(Session::getPluralNumber()) ), - 'datatype' => 'bool' + 'datatype' => 'bool', + 'joinparams' => [ + 'jointype' => 'itemtypeonly', + 'table' => $this->getTable(), + 'condition' => [ + 'NEWTABLE.visible_itemtype' => 'SoftwareVersion', + 'NEWTABLE.items_id' => new QueryExpression('REFTABLE.id') + ] + ] ]; $tab[] = [ 'id' => '23', - 'table' => $this->getTable(), - 'field' => 'is_visible_monitor', + 'table' => DropdownVisibility::getTable(), + 'field' => 'is_visible', 'name' => sprintf(__('%1$s - %2$s'), __('Visibility'), Monitor::getTypeName(Session::getPluralNumber())), - 'datatype' => 'bool' + 'datatype' => 'bool', + 'joinparams' => [ + 'jointype' => 'itemtypeonly', + 'table' => $this->getTable(), + 'condition' => [ + 'NEWTABLE.visible_itemtype' => 'Monitor', + 'NEWTABLE.items_id' => new QueryExpression('REFTABLE.id') + ] + ] ]; $tab[] = [ 'id' => '24', - 'table' => $this->getTable(), - 'field' => 'is_visible_printer', + 'table' => DropdownVisibility::getTable(), + 'field' => 'is_visible', 'name' => sprintf(__('%1$s - %2$s'), __('Visibility'), Printer::getTypeName(Session::getPluralNumber())), - 'datatype' => 'bool' + 'datatype' => 'bool', + 'joinparams' => [ + 'jointype' => 'itemtypeonly', + 'table' => $this->getTable(), + 'condition' => [ + 'NEWTABLE.visible_itemtype' => 'Printer', + 'NEWTABLE.items_id' => new QueryExpression('REFTABLE.id') + ] + ] ]; $tab[] = [ 'id' => '25', - 'table' => $this->getTable(), - 'field' => 'is_visible_peripheral', + 'table' => DropdownVisibility::getTable(), + 'field' => 'is_visible', 'name' => sprintf(__('%1$s - %2$s'), __('Visibility'), Peripheral::getTypeName(Session::getPluralNumber())), - 'datatype' => 'bool' + 'datatype' => 'bool', + 'joinparams' => [ + 'jointype' => 'itemtypeonly', + 'table' => $this->getTable(), + 'condition' => [ + 'NEWTABLE.visible_itemtype' => 'Peripheral', + 'NEWTABLE.items_id' => new QueryExpression('REFTABLE.id') + ] + ] ]; $tab[] = [ 'id' => '26', - 'table' => $this->getTable(), - 'field' => 'is_visible_phone', + 'table' => DropdownVisibility::getTable(), + 'field' => 'is_visible', 'name' => sprintf(__('%1$s - %2$s'), __('Visibility'), Phone::getTypeName(Session::getPluralNumber())), - 'datatype' => 'bool' + 'datatype' => 'bool', + 'joinparams' => [ + 'jointype' => 'itemtypeonly', + 'table' => $this->getTable(), + 'condition' => [ + 'NEWTABLE.visible_itemtype' => 'Phone', + 'NEWTABLE.items_id' => new QueryExpression('REFTABLE.id') + ] + ] ]; $tab[] = [ 'id' => '27', - 'table' => $this->getTable(), - 'field' => 'is_visible_networkequipment', + 'table' => DropdownVisibility::getTable(), + 'field' => 'is_visible', 'name' => sprintf( __('%1$s - %2$s'), __('Visibility'), NetworkEquipment::getTypeName(Session::getPluralNumber()) ), - 'datatype' => 'bool' + 'datatype' => 'bool', + 'joinparams' => [ + 'jointype' => 'itemtypeonly', + 'table' => $this->getTable(), + 'condition' => [ + 'NEWTABLE.visible_itemtype' => 'NetworkEquipment', + 'NEWTABLE.items_id' => new QueryExpression('REFTABLE.id') + ] + ] ]; $tab[] = [ 'id' => '28', - 'table' => $this->getTable(), - 'field' => 'is_visible_softwarelicense', + 'table' => DropdownVisibility::getTable(), + 'field' => 'is_visible', 'name' => sprintf( __('%1$s - %2$s'), __('Visibility'), SoftwareLicense::getTypeName(Session::getPluralNumber()) ), - 'datatype' => 'bool' + 'datatype' => 'bool', + 'joinparams' => [ + 'jointype' => 'itemtypeonly', + 'table' => $this->getTable(), + 'condition' => [ + 'NEWTABLE.visible_itemtype' => 'SoftwareLicense', + 'NEWTABLE.items_id' => new QueryExpression('REFTABLE.id') + ] + ] ]; $tab[] = [ 'id' => '29', - 'table' => $this->getTable(), - 'field' => 'is_visible_certificate', + 'table' => DropdownVisibility::getTable(), + 'field' => 'is_visible', 'name' => sprintf( __('%1$s - %2$s'), __('Visibility'), Certificate::getTypeName(Session::getPluralNumber()) ), - 'datatype' => 'bool' + 'datatype' => 'bool', + 'joinparams' => [ + 'jointype' => 'itemtypeonly', + 'table' => $this->getTable(), + 'condition' => [ + 'NEWTABLE.visible_itemtype' => 'Certificate', + 'NEWTABLE.items_id' => new QueryExpression('REFTABLE.id') + ] + ] ]; $tab[] = [ 'id' => '30', - 'table' => $this->getTable(), - 'field' => 'is_visible_rack', + 'table' => DropdownVisibility::getTable(), + 'field' => 'is_visible', 'name' => sprintf( __('%1$s - %2$s'), __('Visibility'), Rack::getTypeName(Session::getPluralNumber()) ), - 'datatype' => 'bool' + 'datatype' => 'bool', + 'joinparams' => [ + 'jointype' => 'itemtypeonly', + 'table' => $this->getTable(), + 'condition' => [ + 'NEWTABLE.visible_itemtype' => 'Rack', + 'NEWTABLE.items_id' => new QueryExpression('REFTABLE.id') + ] + ] ]; $tab[] = [ 'id' => '31', - 'table' => $this->getTable(), - 'field' => 'is_visible_line', + 'table' => DropdownVisibility::getTable(), + 'field' => 'is_visible', 'name' => sprintf( __('%1$s - %2$s'), __('Visibility'), Line::getTypeName(Session::getPluralNumber()) ), - 'datatype' => 'bool' + 'datatype' => 'bool', + 'joinparams' => [ + 'jointype' => 'itemtypeonly', + 'table' => $this->getTable(), + 'condition' => [ + 'NEWTABLE.visible_itemtype' => 'Line', + 'NEWTABLE.items_id' => new QueryExpression('REFTABLE.id') + ] + ] ]; $tab[] = [ 'id' => '32', - 'table' => $this->getTable(), - 'field' => 'is_visible_enclosure', + 'table' => DropdownVisibility::getTable(), + 'field' => 'is_visible', 'name' => sprintf( __('%1$s - %2$s'), __('Visibility'), Enclosure::getTypeName(Session::getPluralNumber()) ), - 'datatype' => 'bool' + 'datatype' => 'bool', + 'joinparams' => [ + 'jointype' => 'itemtypeonly', + 'table' => $this->getTable(), + 'condition' => [ + 'NEWTABLE.visible_itemtype' => 'Enclosure', + 'NEWTABLE.items_id' => new QueryExpression('REFTABLE.id') + ] + ] ]; $tab[] = [ 'id' => '33', - 'table' => $this->getTable(), - 'field' => 'is_visible_pdu', + 'table' => DropdownVisibility::getTable(), + 'field' => 'is_visible', 'name' => sprintf( __('%1$s - %2$s'), __('Visibility'), PDU::getTypeName(Session::getPluralNumber()) ), - 'datatype' => 'bool' + 'datatype' => 'bool', + 'joinparams' => [ + 'jointype' => 'itemtypeonly', + 'table' => $this->getTable(), + 'condition' => [ + 'NEWTABLE.visible_itemtype' => 'PDU', + 'NEWTABLE.items_id' => new QueryExpression('REFTABLE.id') + ] + ] ]; $tab[] = [ 'id' => '34', - 'table' => $this->getTable(), - 'field' => 'is_visible_cluster', + 'table' => DropdownVisibility::getTable(), + 'field' => 'is_visible', 'name' => sprintf( __('%1$s - %2$s'), __('Visibility'), Cluster::getTypeName(Session::getPluralNumber()) ), - 'datatype' => 'bool' + 'datatype' => 'bool', + 'joinparams' => [ + 'jointype' => 'itemtypeonly', + 'table' => $this->getTable(), + 'condition' => [ + 'NEWTABLE.visible_itemtype' => 'Cluster', + 'NEWTABLE.items_id' => new QueryExpression('REFTABLE.id') + ] + ] ]; $tab[] = [ 'id' => '35', - 'table' => $this->getTable(), - 'field' => 'is_visible_passivedcequipment', + 'table' => DropdownVisibility::getTable(), + 'field' => 'is_visible', 'name' => sprintf( __('%1$s - %2$s'), __('Visibility'), PassiveDCEquipment::getTypeName(Session::getPluralNumber()) ), - 'datatype' => 'bool' + 'datatype' => 'bool', + 'joinparams' => [ + 'jointype' => 'itemtypeonly', + 'table' => $this->getTable(), + 'condition' => [ + 'NEWTABLE.visible_itemtype' => 'PassiveDCEquipment', + 'NEWTABLE.items_id' => new QueryExpression('REFTABLE.id') + ] + ] ]; $tab[] = [ 'id' => '36', - 'table' => $this->getTable(), - 'field' => 'is_visible_contract', + 'table' => DropdownVisibility::getTable(), + 'field' => 'is_visible', 'name' => sprintf( __('%1$s - %2$s'), __('Visibility'), Contract::getTypeName(Session::getPluralNumber()) ), - 'datatype' => 'bool' + 'datatype' => 'bool', + 'joinparams' => [ + 'jointype' => 'itemtypeonly', + 'table' => $this->getTable(), + 'condition' => [ + 'NEWTABLE.visible_itemtype' => 'Contract', + 'NEWTABLE.items_id' => new QueryExpression('REFTABLE.id') + ] + ] ]; $tab[] = [ 'id' => '37', - 'table' => $this->getTable(), - 'field' => 'is_visible_appliance', + 'table' => DropdownVisibility::getTable(), + 'field' => 'is_visible', 'name' => sprintf( __('%1$s - %2$s'), __('Visibility'), Appliance::getTypeName(Session::getPluralNumber()) ), - 'datatype' => 'bool' + 'datatype' => 'bool', + 'joinparams' => [ + 'jointype' => 'itemtypeonly', + 'table' => $this->getTable(), + 'condition' => [ + 'NEWTABLE.visible_itemtype' => 'Appliance', + 'NEWTABLE.items_id' => new QueryExpression('REFTABLE.id') + ] + ] ]; $tab[] = [ 'id' => '38', - 'table' => $this->getTable(), - 'field' => 'is_visible_cable', + 'table' => DropdownVisibility::getTable(), + 'field' => 'is_visible', 'name' => sprintf( __('%1$s - %2$s'), __('Visibility'), Cable::getTypeName(Session::getPluralNumber()) ), - 'datatype' => 'bool' + 'datatype' => 'bool', + 'joinparams' => [ + 'jointype' => 'itemtypeonly', + 'table' => $this->getTable(), + 'condition' => [ + 'NEWTABLE.visible_itemtype' => 'Cable', + 'NEWTABLE.items_id' => new QueryExpression('REFTABLE.id') + ] + ] ]; $tab[] = [ 'id' => '39', - 'table' => $this->getTable(), - 'field' => 'is_visible_databaseinstance', + 'table' => DropdownVisibility::getTable(), + 'field' => 'is_visible', 'name' => sprintf( __('%1$s - %2$s'), __('Visibility'), DatabaseInstance::getTypeName(Session::getPluralNumber()) ), - 'datatype' => 'bool' + 'datatype' => 'bool', + 'joinparams' => [ + 'jointype' => 'itemtypeonly', + 'table' => $this->getTable(), + 'condition' => [ + 'NEWTABLE.visible_itemtype' => 'DatabaseInstance', + 'NEWTABLE.items_id' => new QueryExpression('REFTABLE.id') + ] + ] ]; $tab[] = [ @@ -659,4 +846,19 @@ public function getCloneRelations(): array { return []; } + + public function post_getFromDB() + { + $statevisibility = new DropdownVisibility(); + + foreach ($this->getvisibilityFields() as $visibility_field) { + // Default value for fields that may not be yet stored in DB. + $this->fields[$visibility_field] = 0; + } + + $visibilities = $statevisibility->find(['itemtype' => State::getType(), 'items_id' => $this->fields['id']]); + foreach ($visibilities as $visibility) { + $this->fields['is_visible_' . strtolower($visibility['visible_itemtype'])] = $visibility['is_visible']; + } + } } diff --git a/src/Unmanaged.php b/src/Unmanaged.php index 405e4680dd4..fbad6679265 100644 --- a/src/Unmanaged.php +++ b/src/Unmanaged.php @@ -42,6 +42,7 @@ class Unmanaged extends CommonDBTM { use Glpi\Features\Inventoriable; + use Glpi\Features\State; // From CommonDBTM public $dohistory = true; @@ -186,11 +187,11 @@ public function rawSearchOptions() $tab[] = [ 'id' => '31', - 'table' => 'glpi_states', + 'table' => State::getTable(), 'field' => 'completename', 'name' => __('Status'), 'datatype' => 'dropdown', - 'condition' => ['is_visible_unmanaged' => 1] + 'condition' => $this->getStateVisibilityCriteria() ]; return $tab; diff --git a/templates/components/form/item_device.html.twig b/templates/components/form/item_device.html.twig index df8919f238a..cbe6a42b938 100644 --- a/templates/components/form/item_device.html.twig +++ b/templates/components/form/item_device.html.twig @@ -182,8 +182,7 @@ ) }} {% endif %} - {% if item.isField('states_id') %} - {% set condition = item.getType() in config('state_types') ? {('is_visible_' ~ item.getType()|lower): 1} : {} %} + {% if item.isField('states_id') and item is usingtrait('Glpi\\Features\\State') %} {{ fields.dropdownField( 'State', 'states_id', @@ -191,7 +190,7 @@ __('Status'), field_options|merge({ 'entity': item.fields['entities_id'], - 'condition': condition + 'condition': item.getStateVisibilityCriteria() }) ) }} {% endif %} diff --git a/templates/generic_show_form.html.twig b/templates/generic_show_form.html.twig index aa4268f8902..01eab96ed69 100644 --- a/templates/generic_show_form.html.twig +++ b/templates/generic_show_form.html.twig @@ -97,8 +97,7 @@ {% endif %} {% endif %} - {% if item.isField('states_id') %} - {% set condition = item.getType() in config('state_types') ? {('is_visible_' ~ item.getType()|lower): 1} : {} %} + {% if item.isField('states_id') and item is usingtrait('Glpi\\Features\\State') %} {{ fields.dropdownField( 'State', 'states_id', @@ -106,7 +105,7 @@ __('Status'), field_options|merge({ 'entity': item.fields['entities_id'], - 'condition': condition + 'condition': item.getStateVisibilityCriteria() }) ) }} {% endif %} diff --git a/tests/functional/Dropdown.php b/tests/functional/Dropdown.php index 9950015d751..ead9c966300 100644 --- a/tests/functional/Dropdown.php +++ b/tests/functional/Dropdown.php @@ -1921,8 +1921,9 @@ public function testClone($dropdown_class) $input['items_id'] = 1; } $this->integer($original_items_id = $item->add($input))->isGreaterThan(0); + $original_fields = $item->fields; $this->integer($item->clone())->isNotEqualTo($original_items_id); - foreach ($input as $field => $value) { + foreach ($original_fields as $field => $value) { $this->variable($item->fields[$field])->isEqualTo($value); } } diff --git a/tests/functional/Glpi/Api/HL/Controller/GraphQLController.php b/tests/functional/Glpi/Api/HL/Controller/GraphQLController.php index 100a5b1bfae..40668cf5a3a 100644 --- a/tests/functional/Glpi/Api/HL/Controller/GraphQLController.php +++ b/tests/functional/Glpi/Api/HL/Controller/GraphQLController.php @@ -195,4 +195,57 @@ public function testFullSchemaReplacement() }); }); } + + /** + * Tests a case where there are scalar joins inside an already-joined field (status in this case). + * @return void + */ + public function testGetStateVisibilities() + { + $state = new \State(); + $this->integer($states_id = $state->add([ + 'name' => __FUNCTION__, + 'entities_id' => getItemByTypeName('Entity', '_test_root_entity', true), + 'is_visible_computer' => 1, + 'is_visible_monitor' => 0 + ]))->isGreaterThan(0); + $computer = new \Computer(); + $this->integer($computers_id = $computer->add([ + 'name' => __FUNCTION__, + 'entities_id' => getItemByTypeName('Entity', '_test_root_entity', true), + 'states_id' => $states_id + ]))->isGreaterThan(0); + + $this->login(); + $request = new Request('POST', '/GraphQL', [], <<api->call($request, function ($call) { + /** @var \HLAPICallAsserter $call */ + $call->response + ->status(fn ($status) => $this->integer($status)->isEqualTo(200)) + ->jsonContent(function ($content) { + $this->array($content['data'])->hasSize(1); + $this->array($content['data']['Computer'])->hasSize(1); + $this->array($content['data']['Computer'][0])->hasKey('id'); + $this->array($content['data']['Computer'][0])->hasKey('name'); + $this->array($content['data']['Computer'][0])->hasKey('status'); + $this->array($content['data']['Computer'][0]['status'])->hasKey('name'); + $this->boolean($content['data']['Computer'][0]['status']['visibilities']['computer'])->isTrue(); + $this->boolean($content['data']['Computer'][0]['status']['visibilities']['monitor'])->isFalse(); + }); + }); + } } diff --git a/tests/functional/Glpi/Inventory/Inventory.php b/tests/functional/Glpi/Inventory/Inventory.php index 77cec63b07f..3a244d04c46 100644 --- a/tests/functional/Glpi/Inventory/Inventory.php +++ b/tests/functional/Glpi/Inventory/Inventory.php @@ -5528,6 +5528,7 @@ public function testImportPhone() 'entities_id' => 0, 'is_recursive' => 0, 'locations_id' => 0, + 'states_id' => 0, ], [ 'items_id' => $phones_id, 'itemtype' => 'Phone', @@ -5537,6 +5538,7 @@ public function testImportPhone() 'entities_id' => 0, 'is_recursive' => 0, 'locations_id' => 0, + 'states_id' => 0, ] ] ]; diff --git a/tests/functional/Search.php b/tests/functional/Search.php index f84d08498ce..07c5f7141f9 100644 --- a/tests/functional/Search.php +++ b/tests/functional/Search.php @@ -2296,6 +2296,9 @@ public function testSearchAllAssets() public function testSearchWithNamespacedItem() { + /** @var array $CFG_GLPI */ + global $CFG_GLPI; + $search_params = [ 'is_deleted' => 0, 'start' => 0, @@ -2304,6 +2307,7 @@ public function testSearchWithNamespacedItem() $this->login(); $this->setEntity('_test_root_entity', true); + $CFG_GLPI['state_types'][] = 'SearchTest\\Computer'; $data = $this->doSearch('SearchTest\\Computer', $search_params); $this->string($data['sql']['search']) diff --git a/tests/functional/State.php b/tests/functional/State.php index 93b0ccaf591..24002669b9d 100644 --- a/tests/functional/State.php +++ b/tests/functional/State.php @@ -35,12 +35,18 @@ namespace tests\units; +use CommonDBTM; +use Computer; use DbTestCase; -use Generator; +use DropdownVisibility; +use Phone; +use Printer; +use ReflectionClass; +use Toolbox; class State extends DbTestCase { - protected function testIsUniqueProvider(): Generator + protected function testIsUniqueProvider(): iterable { // Insert test data $this->createItems("State", [ @@ -78,9 +84,167 @@ protected function testIsUniqueProvider(): Generator /** * @dataprovider testIsUniqueProvider */ - public function testIsUnique(array $input, bool $expected) + public function testIsUnique(array $input, bool $expected): void { $state = new \State(); $this->boolean($state->isUnique($input))->isEqualTo($expected); } + + public function testVisibility(): void + { + /** @var array $CFG_GLPI */ + global $CFG_GLPI; + + $state = new \State(); + + $states_id = $state->add([ + 'name' => 'Test computer and phone', + 'is_visible_computer' => '1', + 'is_visible_phone' => '1', + ]); + + $this->integer($states_id)->isGreaterThan(0); + + $statevisibility = new DropdownVisibility(); + $visibilities = $statevisibility->find(['itemtype' => \State::getType(), 'items_id' => $states_id]); + $this->array($visibilities)->hasSize(2); + $this->boolean($statevisibility->getFromDBByCrit(['itemtype' => \State::getType(), 'items_id' => $states_id, 'visible_itemtype' => Computer::getType(), 'is_visible' => 1]))->isTrue(); + $this->boolean($statevisibility->getFromDBByCrit(['itemtype' => \State::getType(), 'items_id' => $states_id, 'visible_itemtype' => Phone::getType(), 'is_visible' => 1]))->isTrue(); + $this->boolean($statevisibility->getFromDBByCrit(['itemtype' => \State::getType(), 'items_id' => $states_id, 'visible_itemtype' => Printer::getType(), 'is_visible' => 0]))->isFalse(); + + $state->update([ + 'id' => $states_id, + 'is_visible_computer' => '0', + 'is_visible_printer' => '1', + ]); + $visibilities = $statevisibility->find(['itemtype' => \State::getType(), 'items_id' => $states_id]); + $this->array($visibilities)->hasSize(3); + $visibilities = $statevisibility->find(['itemtype' => \State::getType(), 'items_id' => $states_id, 'is_visible' => 1]); + $this->array($visibilities)->hasSize(2); + $this->boolean($statevisibility->getFromDBByCrit(['itemtype' => \State::getType(), 'items_id' => $states_id, 'visible_itemtype' => Computer::getType(), 'is_visible' => 0]))->isTrue(); + $this->boolean($statevisibility->getFromDBByCrit(['itemtype' => \State::getType(), 'items_id' => $states_id, 'visible_itemtype' => Phone::getType(), 'is_visible' => 1]))->isTrue(); + $this->boolean($statevisibility->getFromDBByCrit(['itemtype' => \State::getType(), 'items_id' => $states_id, 'visible_itemtype' => Printer::getType(), 'is_visible' => 1]))->isTrue(); + + $this->boolean($state->getFromDB($states_id))->isTrue(); + $this->string($state->fields['name'])->isEqualTo('Test computer and phone'); + + $expected_values = []; + // Default values + foreach ($CFG_GLPI['state_types'] as $type) { + $expected_values['is_visible_' . strtolower($type)] = 0; + } + $expected_values['is_visible_computer'] = 0; + $expected_values['is_visible_phone'] = 1; + $expected_values['is_visible_printer'] = 1; + foreach ($expected_values as $field => $expected_value) { + $this->integer($state->fields[$field])->isIdenticalTo($expected_value); + } + } + + public function testHasFeature(): void + { + /** @var array $CFG_GLPI */ + global $CFG_GLPI; + + foreach ($CFG_GLPI['state_types'] as $itemtype) { + $this->boolean(Toolbox::hasTrait($itemtype, \Glpi\Features\State::class))->isTrue( + $itemtype . ' misses ' . \Glpi\Features\State::class . ' trait!' + ); + } + } + + public function testRegisteredTypes(): void + { + /** + * @var array $CFG_GLPI + * @var \DBmysql $DB + */ + global $CFG_GLPI, $DB; + + foreach ($CFG_GLPI['state_types'] as $itemtype) { + $this->boolean($DB->fieldExists($itemtype::getTable(), 'states_id'))->isTrue( + $itemtype . ' should have a `states_id` field.' + ); + } + + foreach ($DB->listTables() as $table_data) { + $table_name = $table_data['TABLE_NAME']; + $classname = getItemTypeForTable($table_name); + + if ( + $classname === \State::class + || !is_a($classname, CommonDBTM::class, true) + || (new ReflectionClass($classname))->isAbstract() + ) { + continue; + } + + $has_field = $DB->fieldExists($table_name, 'states_id'); + $this->array($CFG_GLPI['state_types'])->{$has_field ? 'contains' : 'notContains'}( + $classname, + $classname . ' should be declared in `$CFG_GLPI[\'state_types\']`.' + ); + } + } + + public function testIsStateVisible(): void + { + /** @var array $CFG_GLPI */ + global $CFG_GLPI; + + $itemtype = $CFG_GLPI['state_types'][0]; + + $state = new \State(); + $states_id = $state->add([ + 'name' => 'Test computer and phone', + 'is_visible_' . strtolower($itemtype) => '1' + ]); + $this->integer($states_id)->isGreaterThan(0); + + $item = new $itemtype(); + $this->boolean(method_exists($itemtype, 'isStateVisible'))->isTrue($itemtype . ' misses isStateVisible() method!'); + $this->boolean($item->isStateVisible($states_id))->isTrue(); + + unset($CFG_GLPI['state_types'][0]); + $this->boolean(method_exists($itemtype, 'isStateVisible'))->isTrue($itemtype . ' misses isStateVisible() method!'); + + $this->when( + function () use ($item, $states_id) { + $this->boolean($item->isStateVisible($states_id))->isTrue(); + } + ) + ->error() + ->withType(E_USER_ERROR) + ->withMessage(sprintf('Class %s must be present in $CFG_GLPI[\'state_types\']', $itemtype)) + ->exists(); + } + + public function testGetStateVisibilityCriteria(): void + { + /** @var array $CFG_GLPI */ + global $CFG_GLPI; + + $itemtype = $CFG_GLPI['state_types'][0]; + + $item = new $itemtype(); + $this->array($item->getStateVisibilityCriteria())->isIdenticalTo([ + 'LEFT JOIN' => [ + DropdownVisibility::getTable() => [ + 'ON' => [ + DropdownVisibility::getTable() => 'items_id', + \State::getTable() => 'id', [ + 'AND' => [ + DropdownVisibility::getTable() . '.itemtype' => \State::getType() + ] + ] + ] + ] + ], + 'WHERE' => [ + DropdownVisibility::getTable() . '.itemtype' => \State::getType(), + DropdownVisibility::getTable() . '.visible_itemtype' => $itemtype, + DropdownVisibility::getTable() . '.is_visible' => 1 + ] + ]); + } } diff --git a/tests/units/DBmysqlIterator.php b/tests/units/DBmysqlIterator.php index 44f7e3e2593..043a9c3e0be 100644 --- a/tests/units/DBmysqlIterator.php +++ b/tests/units/DBmysqlIterator.php @@ -480,7 +480,50 @@ function () { 'SELECT * FROM `foo` LEFT JOIN `bar` ON (`bar`.`id` = `foo`.`fk` AND `field` = \'42\')' ); - //test derived table in JOIN statement + //order in fkey should not matter + $it = $this->it->execute( + 'foo', + [ + 'LEFT JOIN' => [ + 'bar' => [ + 'FKEY' => [ + [ + 'AND' => ['field' => 42] + ], + 'bar' => 'id', + 'foo' => 'fk' + ] + ] + ] + ] + ); + $this->string($it->getSql())->isIdenticalTo( + 'SELECT * FROM `foo` LEFT JOIN `bar` ON (`bar`.`id` = `foo`.`fk` AND `field` = \'42\')' + ); + + //condition set as associative array should work also + $it = $this->it->execute( + 'foo', + [ + 'LEFT JOIN' => [ + 'bar' => [ + 'FKEY' => [ + 'bar' => 'id', + 'foo' => 'fk', + 'acondition' => [ + 'AND' => ['field' => 42] + ] + ] + ] + ] + ] + ); + $this->string($it->getSql())->isIdenticalTo( + 'SELECT * FROM `foo` LEFT JOIN `bar` ON (`bar`.`id` = `foo`.`fk` AND `field` = \'42\')' + ); + + + //test derived table in JOIN statement $it = $this->it->execute( 'foo', [