diff --git a/hook.php b/hook.php index 84f77f2f..3bc045a7 100644 --- a/hook.php +++ b/hook.php @@ -28,6 +28,8 @@ * ------------------------------------------------------------------------- */ +use Glpi\Api\HL\Doc\Schema; + /** * Plugin install process * @@ -297,7 +299,7 @@ function plugin_fields_rule_matched($params = []) function plugin_fields_giveItem($itemtype, $ID, $data, $num) { - $searchopt = &Search::getOptions($itemtype); + $searchopt = Search::getOptions($itemtype); $table = $searchopt[$ID]["table"]; //fix glpi default Search::giveItem who for empty date display "--" @@ -332,3 +334,126 @@ function plugin_datainjection_populate_fields() } } } + +function plugin_fields_redefine_api_schemas(array $data): array +{ + global $DB; + + $fn_fieldTypeToAPIType = static function (string $type): array { + $type = explode('-', $type)[0]; + return match ($type) { + 'number' => [Schema::TYPE_NUMBER, Schema::FORMAT_NUMBER_FLOAT], + 'yesno' => [Schema::TYPE_BOOLEAN, Schema::FORMAT_BOOLEAN_BOOLEAN], + 'date' => [Schema::TYPE_STRING, Schema::FORMAT_STRING_DATE], + 'datetime' => [Schema::TYPE_STRING, Schema::FORMAT_STRING_DATE_TIME], + default => [Schema::TYPE_STRING, Schema::FORMAT_STRING_STRING], + }; + }; + + $new_schemas = []; + + foreach ($data['schemas'] as &$schema) { + if (!isset($schema['x-itemtype'])) { + continue; + } + $itemtype = $schema['x-itemtype']; + $schema_name = $itemtype . '_CustomFields'; + //Note PluginFieldsContainer::findContainer already checks permissions + $container_id = PluginFieldsContainer::findContainer($schema['x-itemtype'], 'dom'); + if ($container_id !== null) { + $it = $DB->request([ + 'SELECT' => [ + 'glpi_plugin_fields_fields.*', + 'glpi_plugin_fields_containers.name AS container_name', + ], + 'FROM' => 'glpi_plugin_fields_fields', + 'LEFT JOIN' => [ + 'glpi_plugin_fields_containers' => [ + 'ON' => [ + 'glpi_plugin_fields_fields' => 'plugin_fields_containers_id', + 'glpi_plugin_fields_containers' => 'id' + ] + ] + ], + 'WHERE' => [ + 'plugin_fields_containers_id' => $container_id, + 'glpi_plugin_fields_fields.is_active' => 1 + ] + ]); + if (count($it)) { + foreach ($it as $field) { + if (!isset($new_schemas[$schema_name])) { + $new_schemas[$schema_name] = [ + 'type' => Schema::TYPE_OBJECT, + 'properties' => [] + ]; + } + $type_format = $fn_fieldTypeToAPIType($field['type']); + $table = strtolower("glpi_plugin_fields_{$schema['x-itemtype']}{$field['container_name']}s"); + $sql_field = $field['name']; + if (str_starts_with($field['type'], 'dropdown')) { + if (str_starts_with($field['type'], 'dropdown-')) { + $dropdown_type = explode('-', $field['type'], 2)[1]; + } else { + $dropdown_type = 'PluginFields' . ucfirst($field['name']) . 'Dropdown'; + } + $is_tree = is_subclass_of($dropdown_type, CommonTreeDropdown::class); + $dropdown_fk = $field['type'] === 'dropdown' ? $dropdown_type::getForeignKeyField() : $field['name']; + $new_schemas[$schema_name]['properties'][$field['name']] = [ + 'type' => Schema::TYPE_OBJECT, + 'x-join' => [ + 'table' => $dropdown_type::getTable(), // This is the table with the desired values + 'field' => 'id', + 'fkey' => $dropdown_fk, + 'ref_join' => [ + 'table' => $table, + 'fkey' => 'id', + 'field' => 'items_id', + 'condition' => [ + 'itemtype' => $schema['x-itemtype'], + ] + ], + ], + 'properties' => [ + 'id' => [ + 'type' => Schema::TYPE_INTEGER, + 'format' => Schema::FORMAT_INTEGER_INT64, + 'x-readonly' => true, + ], + 'value' => [ + 'type' => Schema::TYPE_STRING, + 'x-field' => $is_tree ? 'completename' : 'name', + ] + ] + ]; + } else { + $new_schemas[$schema_name]['properties'][$field['name']] = [ + 'type' => $type_format[0], + 'format' => $type_format[1], + 'x-join' => [ + // This is the table with the desired values + 'table' => $table, + 'fkey' => 'id', + 'field' => 'items_id', + 'condition' => [ + 'itemtype' => $schema['x-itemtype'], + ] + ], + 'x-field' => $sql_field, + 'x-readonly' => true + ]; + } + } + if (isset($new_schemas[$schema_name]) && count($new_schemas[$schema_name]['properties']) > 0) { + $schema['properties']['custom_fields'] = [ + 'type' => Schema::TYPE_OBJECT, + 'x-full-schema' => $schema_name, + 'properties' => $new_schemas[$schema_name]['properties'], + ]; + } + } + } + } + $data['schemas'] = array_merge($data['schemas'], $new_schemas); + return $data; +} diff --git a/setup.php b/setup.php index 42b187f5..84e7c92a 100644 --- a/setup.php +++ b/setup.php @@ -33,7 +33,7 @@ // Minimal GLPI version, inclusive define("PLUGIN_FIELDS_MIN_GLPI", "10.0.0"); // Maximum GLPI version, exclusive -define("PLUGIN_FIELDS_MAX_GLPI", "10.0.99"); +define("PLUGIN_FIELDS_MAX_GLPI", "10.1.99"); if (!defined("PLUGINFIELDS_DIR")) { define("PLUGINFIELDS_DIR", Plugin::getPhpDir("fields")); @@ -63,6 +63,7 @@ mkdir(PLUGINFIELDS_FRONT_PATH); } +use Glpi\Plugin\Hooks; use Symfony\Component\Yaml\Yaml; /** @@ -85,105 +86,109 @@ function plugin_init_fields() $pluginfields_autoloader = new PluginFieldsAutoloader([PLUGINFIELDS_CLASS_PATH]); $pluginfields_autoloader->register(); - if (Session::getLoginUserID() && Plugin::isPluginActive('fields')) { - // Init hook about itemtype(s) for plugin fields - if (!isset($PLUGIN_HOOKS['plugin_fields'])) { - $PLUGIN_HOOKS['plugin_fields'] = []; - } + if (Plugin::isPluginActive('fields')) { + // This API integration cannot be done inside a login check since the plugin is initialized before the Router handles authentication + $PLUGIN_HOOKS[Hooks::REDEFINE_API_SCHEMAS]['fields'] = 'plugin_fields_redefine_api_schemas'; + if (Session::getLoginUserID()) { + // Init hook about itemtype(s) for plugin fields + if (!isset($PLUGIN_HOOKS['plugin_fields'])) { + $PLUGIN_HOOKS['plugin_fields'] = []; + } - // When a Category is changed during ticket creation - if ( - isset($_POST) && !empty($_POST) - && isset($_POST['_plugin_fields_type']) - && $_SERVER['REQUEST_URI'] ?? '' == Ticket::getFormURL() - ) { - foreach ($_POST as $key => $value) { - if (!is_array($value)) { - $_SESSION['plugin']['fields']['values_sent'][$key] = $value; + // When a Category is changed during ticket creation + if ( + isset($_POST) && !empty($_POST) + && isset($_POST['_plugin_fields_type']) + && $_SERVER['REQUEST_URI'] ?? '' == Ticket::getFormURL() + ) { + foreach ($_POST as $key => $value) { + if (!is_array($value)) { + $_SESSION['plugin']['fields']['values_sent'][$key] = $value; + } } } - } - if (Plugin::isPluginActive('fusioninventory')) { - $PLUGIN_HOOKS['fusioninventory_inventory']['fields'] - = ['PluginFieldsInventory', 'updateInventory']; - } + if (Plugin::isPluginActive('fusioninventory')) { + $PLUGIN_HOOKS['fusioninventory_inventory']['fields'] + = ['PluginFieldsInventory', 'updateInventory']; + } - // complete rule engine - $PLUGIN_HOOKS['use_rules']['fields'] = ['PluginFusioninventoryTaskpostactionRule']; - $PLUGIN_HOOKS['rule_matched']['fields'] = 'plugin_fields_rule_matched'; + // complete rule engine + $PLUGIN_HOOKS['use_rules']['fields'] = ['PluginFusioninventoryTaskpostactionRule']; + $PLUGIN_HOOKS['rule_matched']['fields'] = 'plugin_fields_rule_matched'; - if (isset($_SESSION['glpiactiveentities'])) { - // add link in plugin page - $PLUGIN_HOOKS['config_page']['fields'] = 'front/container.php'; + if (isset($_SESSION['glpiactiveentities'])) { + // add link in plugin page + $PLUGIN_HOOKS['config_page']['fields'] = 'front/container.php'; - // add entry to configuration menu - $PLUGIN_HOOKS["menu_toadd"]['fields'] = ['config' => 'PluginFieldsMenu']; + // add entry to configuration menu + $PLUGIN_HOOKS["menu_toadd"]['fields'] = ['config' => 'PluginFieldsMenu']; - // add tabs to itemtypes - $itemtypes = array_unique(PluginFieldsContainer::getEntries()); - if (count($itemtypes) > 0) { - Plugin::registerClass( - 'PluginFieldsContainer', - ['addtabon' => $itemtypes] - ); - } + // add tabs to itemtypes + $itemtypes = array_unique(PluginFieldsContainer::getEntries()); + if (count($itemtypes) > 0) { + Plugin::registerClass( + 'PluginFieldsContainer', + ['addtabon' => $itemtypes] + ); + } - //include js and css - $debug = (isset($_SESSION['glpi_use_mode']) - && $_SESSION['glpi_use_mode'] == Session::DEBUG_MODE ? true : false); - if (!$debug && file_exists(__DIR__ . '/css/fields.min.css')) { - $PLUGIN_HOOKS['add_css']['fields'][] = 'css/fields.min.css'; - } else { - $PLUGIN_HOOKS['add_css']['fields'][] = 'css/fields.css'; - } + //include js and css + $debug = (isset($_SESSION['glpi_use_mode']) + && $_SESSION['glpi_use_mode'] == Session::DEBUG_MODE ? true : false); + if (!$debug && file_exists(__DIR__ . '/css/fields.min.css')) { + $PLUGIN_HOOKS['add_css']['fields'][] = 'css/fields.min.css'; + } else { + $PLUGIN_HOOKS['add_css']['fields'][] = 'css/fields.css'; + } - // Add/delete profiles to automaticaly to container - $PLUGIN_HOOKS['item_add']['fields']['Profile'] = ["PluginFieldsProfile", "addNewProfile"]; - $PLUGIN_HOOKS['pre_item_purge']['fields']['Profile'] = ["PluginFieldsProfile", "deleteProfile"]; + // Add/delete profiles to automaticaly to container + $PLUGIN_HOOKS['item_add']['fields']['Profile'] = ["PluginFieldsProfile", "addNewProfile"]; + $PLUGIN_HOOKS['pre_item_purge']['fields']['Profile'] = ["PluginFieldsProfile", "deleteProfile"]; - //load drag and drop javascript library on Package Interface - $PLUGIN_HOOKS['add_javascript']['fields'][] = "lib/redips-drag-min.js"; - if (!$debug && file_exists(__DIR__ . '/js/drag-field-row.min.js')) { - $PLUGIN_HOOKS['add_javascript']['fields'][] = 'js/drag-field-row.min.js'; - } else { - $PLUGIN_HOOKS['add_javascript']['fields'][] = 'js/drag-field-row.js'; + //load drag and drop javascript library on Package Interface + $PLUGIN_HOOKS['add_javascript']['fields'][] = "lib/redips-drag-min.js"; + if (!$debug && file_exists(__DIR__ . '/js/drag-field-row.min.js')) { + $PLUGIN_HOOKS['add_javascript']['fields'][] = 'js/drag-field-row.min.js'; + } else { + $PLUGIN_HOOKS['add_javascript']['fields'][] = 'js/drag-field-row.js'; + } } - } - // Add Fields to Datainjection - if (Plugin::isPluginActive('datainjection')) { - $PLUGIN_HOOKS['plugin_datainjection_populate']['fields'] = "plugin_datainjection_populate_fields"; - } + // Add Fields to Datainjection + if (Plugin::isPluginActive('datainjection')) { + $PLUGIN_HOOKS['plugin_datainjection_populate']['fields'] = "plugin_datainjection_populate_fields"; + } - //Retrieve dom container - $itemtypes = PluginFieldsContainer::getUsedItemtypes(); - if ($itemtypes !== false) { - foreach ($itemtypes as $itemtype) { - $PLUGIN_HOOKS['pre_item_update']['fields'][$itemtype] = [ - "PluginFieldsContainer", - "preItemUpdate" - ]; - $PLUGIN_HOOKS['pre_item_add']['fields'][$itemtype] = [ - "PluginFieldsContainer", - "preItem" - ]; - $PLUGIN_HOOKS['item_add']['fields'][$itemtype] = [ - "PluginFieldsContainer", - "postItemAdd" - ]; - $PLUGIN_HOOKS['pre_item_purge'] ['fields'][$itemtype] = [ - "PluginFieldsContainer", - "preItemPurge" - ]; + //Retrieve dom container + $itemtypes = PluginFieldsContainer::getUsedItemtypes(); + if ($itemtypes !== false) { + foreach ($itemtypes as $itemtype) { + $PLUGIN_HOOKS['pre_item_update']['fields'][$itemtype] = [ + "PluginFieldsContainer", + "preItemUpdate" + ]; + $PLUGIN_HOOKS['pre_item_add']['fields'][$itemtype] = [ + "PluginFieldsContainer", + "preItem" + ]; + $PLUGIN_HOOKS['item_add']['fields'][$itemtype] = [ + "PluginFieldsContainer", + "postItemAdd" + ]; + $PLUGIN_HOOKS['pre_item_purge'] ['fields'][$itemtype] = [ + "PluginFieldsContainer", + "preItemPurge" + ]; + } } - } - // Display fields in any existing tab - $PLUGIN_HOOKS['post_item_form']['fields'] = [ - 'PluginFieldsField', - 'showForTab' - ]; + // Display fields in any existing tab + $PLUGIN_HOOKS['post_item_form']['fields'] = [ + 'PluginFieldsField', + 'showForTab' + ]; + } } }