From 2d0a27730fe19cdc3fd2adcbc8deae544450c624 Mon Sep 17 00:00:00 2001 From: Mirko M Date: Thu, 12 Oct 2023 16:20:49 +0200 Subject: [PATCH 1/3] Additional missing injections: virtualmachines, software with versions, disks --- inc/computervirtualmachineinjection.class.php | 170 ++++++++++++++++++ inc/item_diskinjection.class.php | 134 ++++++++++++++ inc/item_softwareversioninjection.class.php | 29 +++ inc/softwareversioninjection.class.php | 29 +++ setup.php | 4 +- 5 files changed, 365 insertions(+), 1 deletion(-) create mode 100644 inc/computervirtualmachineinjection.class.php create mode 100644 inc/item_diskinjection.class.php diff --git a/inc/computervirtualmachineinjection.class.php b/inc/computervirtualmachineinjection.class.php new file mode 100644 index 00000000..6664f4b3 --- /dev/null +++ b/inc/computervirtualmachineinjection.class.php @@ -0,0 +1,170 @@ +. + * ------------------------------------------------------------------------- + @author Wuerth Phoenix + @copyright Copyright (c) 2010-2013 Datainjection plugin team + @copyright Copyright (C) 2017-2022 Wuerth Phoenix, http://www.wuerth-phoenix.com + @license GPLv2+ + http://www.gnu.org/licenses/gpl.txt + @link https://forge.indepnet.net/projects/datainjection + @link http://www.glpi-project.org/ + @since 2009 + ---------------------------------------------------------------------- */ + +if (!defined('GLPI_ROOT')) { + die("Sorry. You can't access directly to this file"); +} + +class PluginDatainjectionComputerVirtualMachineInjection extends ComputerVirtualMachine //MOMI from Item_Diskinjection !! + implements PluginDatainjectionInjectionInterface { + + + static function getTable($classname = null) { + + $parenttype = get_parent_class(); + return $parenttype::getTable(); + } + + + function isPrimaryType() { + return true; + } + + + function connectedTo() { + return array('Computer'); + } + + /** + * @see plugins/datainjection/inc/PluginDatainjectionInjectionInterface::getOptions() + **/ + function getOptions($primary_type='') { + + $tab = Search::getOptions(get_parent_class($this)); + + #these have to be added here because they are defined only in rawSearchOptionsToAdd! + # -- put id as tab[id] and add linkfield. + $tab[161] = [ + 'table' => 'glpi_virtualmachinestates', + 'field' => 'name', + 'name' => __('State'), + 'forcegroupby' => true, + 'massiveaction' => false, + 'datatype' => 'dropdown', + 'joinparams' => [ + 'beforejoin' => [ + 'table' => self::getTable(), + 'joinparams' => [ + 'jointype' => 'child' + ] + ] + ], + 'linkfield' => 'virtualmachinestates_id' + ]; + + $tab[162] = [ + 'table' => 'glpi_virtualmachinesystems', + 'field' => 'name', + 'name' => VirtualMachineSystem::getTypeName(1), + 'forcegroupby' => true, + 'massiveaction' => false, + 'datatype' => 'dropdown', + 'joinparams' => [ + 'beforejoin' => [ + 'table' => self::getTable(), + 'joinparams' => [ + 'jointype' => 'child' + ] + ] + ], + 'linkfield' => 'virtualmachinesystems_id' + ]; + + $tab[163] = [ + 'table' => 'glpi_virtualmachinetypes', + 'field' => 'name', + 'name' => VirtualMachineType::getTypeName(1), + 'datatype' => 'dropdown', + 'forcegroupby' => true, + 'massiveaction' => false, + 'joinparams' => [ + 'beforejoin' => [ + 'table' => self::getTable(), + 'joinparams' => [ + 'jointype' => 'child' + ] + ] + ], + 'linkfield' => 'virtualmachinetypes_id' + ]; + + //Remove some options because some fields cannot be imported + $blacklist = array();# = PluginDatainjectionCommonInjectionLib::getBlacklistedOptions(get_parent_class($this)); + $notimportable = array(); + + $options['ignore_fields'] = array_merge($blacklist, $notimportable); + $options['displaytype'] = ["dropdown" => [161, 162, 163]]; + + return PluginDatainjectionCommonInjectionLib::addToSearchOptions($tab, $options, $this); + } + + /** + * @param $primary_type + * @param $values + **/ + function addSpecificNeededFields($primary_type, $values) { + + $fields = []; + if ($primary_type == 'Computer') { + $fields['computers_id'] = $values[$primary_type]['id']; + } + return $fields; + } + + /** + * @param $fields_toinject array + * @param $options array + **/ + function checkPresent($fields_toinject = [], $options = []) { + if ($options['itemtype'] != 'ComputerVirtualMachine') { + return (" AND `computers_id` = '" . $fields_toinject['Computer']['id'] . "' + AND `name` = '" . $fields_toinject['ComputerVirtualMachine']['name'] . "'"); + } + return ""; + } + + /** + * @param $values + * @param $add (true by default) + * @param $rights array + **/ + + /** + * @see plugins/datainjection/inc/PluginDatainjectionInjectionInterface::addOrUpdateObject() + **/ + function addOrUpdateObject($values = [], $options = []) { + //error_log("THIS IS NEVER EXECUTED BECAUSE A COMPUTER IS ADDED, NOT A VM!!" ); + } + +} diff --git a/inc/item_diskinjection.class.php b/inc/item_diskinjection.class.php new file mode 100644 index 00000000..0df59467 --- /dev/null +++ b/inc/item_diskinjection.class.php @@ -0,0 +1,134 @@ +. + * ------------------------------------------------------------------------- + * @author Wuerth Phoenix + * @copyright Copyright (C) 2017-2023 Wuerth Phoenix, http://www.wuerth-phoenix.com + * @copyright Copyright (C) 2007-2023 by DataInjection plugin team. + * @license GPLv2 https://www.gnu.org/licenses/gpl-2.0.html + * @link https://github.com/pluginsGLPI/datainjection + * ------------------------------------------------------------------------- + */ + +if (!defined('GLPI_ROOT')) { + die("Sorry. You can't access directly to this file"); +} + +class PluginDatainjectionItem_DiskInjection extends Item_Disk + implements PluginDatainjectionInjectionInterface { + + + static function getTable($classname = null) { + + $parenttype = get_parent_class(); + return $parenttype::getTable(); + } + + + function isPrimaryType() { + return true; + } + + + function connectedTo() { + return array('Computer'); + } + + /** + * @see plugins/datainjection/inc/PluginDatainjectionInjectionInterface::getOptions() + **/ + function getOptions($primary_type='') { + + $tab = Search::getOptions(get_parent_class($this)); + + $tab[101]['table'] = $this->getTable(); + $tab[101]['field'] = 'totalsize'; + $tab[101]['linkfield'] = 'totalsize'; + $tab[101]['name'] = __('Global size'); + $tab[101]['datatype'] = 'string'; + $tab[101]['injectable'] = true; + + $tab[102]['table'] = $this->getTable(); + $tab[102]['field'] = 'freesize'; + $tab[102]['linkfield'] = 'freesize'; + $tab[102]['name'] = __('Free size'); + $tab[102]['datatype'] = 'string'; + $tab[102]['injectable'] = true; + + //Remove some options because some fields cannot be imported + $blacklist = PluginDatainjectionCommonInjectionLib::getBlacklistedOptions(get_parent_class($this)); + $notimportable = array(); + + $options['ignore_fields'] = array_merge($blacklist, $notimportable); + $options['displaytype'] = array( "text" => array(101), + "text" => array(102)); + + return PluginDatainjectionCommonInjectionLib::addToSearchOptions($tab, $options, $this); + } + + + /** + * @param $values + * @param $add (true by default) + * @param $rights array + **/ + + function processAfterInsertOrUpdate($values, $add=true, $rights=array()) { + + if (isset($values['Computer']['id'])) { + $class = get_parent_class($this); + $item = new $class(); + + $where = [ + 'itemtype' => 'Computer', + 'name' => $values[$class]['name'], + 'items_id' => $values['Computer']['id'], + ]; + + $tmp['items_id'] = $values['Computer']['id']; + $tmp['itemtype'] = 'Computer'; + + $tmp = $values[$class]; + unset($tmp['id']); + + if (!countElementsInTable($item->getTable(), $where)) { + $item->add($tmp); + } else { + $datas = getAllDataFromTable($item->getTable(), $where); + foreach ($datas as $data) { + $tmp['id'] = $data['id']; + $item->update($tmp); + } + } + } + } + + /** + * @see plugins/datainjection/inc/PluginDatainjectionInjectionInterface::addOrUpdateObject() + **/ + function addOrUpdateObject($values = [], $options = []) { + //error_log("THIS IS NEVER EXECUTED BECAUSE A COMPUTER IS ADDED, NOT A DISK!!" ); + } + +} +?> diff --git a/inc/item_softwareversioninjection.class.php b/inc/item_softwareversioninjection.class.php index 33f46976..f3ee64ab 100644 --- a/inc/item_softwareversioninjection.class.php +++ b/inc/item_softwareversioninjection.class.php @@ -140,5 +140,34 @@ function addSpecificNeededFields($primary_type, $values) { return $fields; } + + function processAfterInsertOrUpdate($values, $add=true, $rights=array()) { + + $class = get_parent_class($this); + $item = new $class(); + $where = [ + 'softwareversions_id'=> $values[$class]['softwareversions_id'], + 'itemtype' => $values[$class]['itemtype'], + 'items_id' => $values[$class]['items_id'], + # I don't search for softwareversions_id, this should be single entry and updated + ]; + + $tmp = $values[$class]; + unset($tmp['id']); + + if (!countElementsInTable($item->getTable(), $where)) { + $item->add($tmp); + } else { + $datas = getAllDataFromTable($item->getTable(), $where); + foreach ($datas as $data) { + //update only first item + if (isset($tmp['id'])) { + continue; + } + $tmp['id'] = $data['id']; + $item->update($tmp); + } + } + } } diff --git a/inc/softwareversioninjection.class.php b/inc/softwareversioninjection.class.php index cfe1701e..73c104e7 100644 --- a/inc/softwareversioninjection.class.php +++ b/inc/softwareversioninjection.class.php @@ -193,4 +193,33 @@ function checkPresent($fields_toinject = [], $options = []) { return ""; } + + function processAfterInsertOrUpdate($values, $add=true, $rights=array()) { + + if (isset($values['Software']['id'])) { + $class = get_parent_class($this); //"SoftwareVersion" + $item = new $class(); + + $where = [ + 'name' => $values[$class]['name'], + 'softwares_id' => $values[$class]['items_id'], + ]; + + $tmp = $values[$class]; + unset($tmp['id']); + + if (!countElementsInTable($item->getTable(), $where)) { + $item->add($tmp); + } else { + $datas = getAllDataFromTable($item->getTable(), $where); + foreach ($datas as $data) { + $tmp['id'] = $data['id']; + $item->update($tmp); + // update only first item (nevertheless there should never be more than one) + break; + } + } + } + } + } diff --git a/setup.php b/setup.php index 6f0f6ed6..20531a0d 100644 --- a/setup.php +++ b/setup.php @@ -214,7 +214,9 @@ function getTypesToInject() { 'PluginDatainjectionDeviceDriveInjection' => 'datainjection', 'PluginDatainjectionDeviceNetworkCardInjection' => 'datainjection', 'PluginDatainjectionApplianceInjection' => 'datainjection', - 'PluginDatainjectionCertificateInjection' => 'datainjection' + 'PluginDatainjectionCertificateInjection' => 'datainjection', + 'PluginDatainjectionItem_DiskInjection' => 'datainjection', + 'PluginDatainjectionComputerVirtualMachineInjection' => 'datainjection' ]; //Add plugins Plugin::doHook('plugin_datainjection_populate'); From bf7696e788ef729892283da067d300d4538a4367 Mon Sep 17 00:00:00 2001 From: Mirko M Date: Fri, 20 Oct 2023 12:52:44 +0200 Subject: [PATCH 2/3] Added schedulable imports with an AutomaticAction and file parameters in injection models. --- README_WP.txt | 26 ++++++ hook.php | 25 +++++- inc/cron.class.php | 201 ++++++++++++++++++++++++++++++++++++++++++++ inc/model.class.php | 23 +++-- 4 files changed, 268 insertions(+), 7 deletions(-) create mode 100644 README_WP.txt create mode 100644 inc/cron.class.php diff --git a/README_WP.txt b/README_WP.txt new file mode 100644 index 00000000..6d249b87 --- /dev/null +++ b/README_WP.txt @@ -0,0 +1,26 @@ + +Datainjection changes WP: + * cron-schedulable in automatic action, with 2 parameters CSV and Enabled Y/N + * added diskinjection and vm injection + * additional code in SoftwareVersionInjection and Iten_softwareversioninjection to make it work + +PHP files changed + +Setup: version, register Diskinjection and VMinjection + +Hook: db csvfilename, enable_scheduled_injection + Register PluginDatainjectionCron + Disable uninstall with return true; +softwareversionversioninjection: ADD processAfterInsertOrUpdate + +model: add getter function getCSVFilename, getEnableScheduledInjection + Rawsearchoptions array_merge, cron forms show, WP -- if (!$webservice && !$unique_filename) + +Item_SoftwareVersionInjection: added processAfterInsertOrUpdate + +New Files: ++++item_diskinjection+++ ++++cron.class+++ ++++computervirtualmachineinjection+++ + + diff --git a/hook.php b/hook.php index 969fe791..344f3ed1 100644 --- a/hook.php +++ b/hook.php @@ -87,6 +87,8 @@ function plugin_datainjection_install() { `float_format` tinyint NOT NULL DEFAULT '0', `port_unicity` tinyint NOT NULL DEFAULT '0', `step` int NOT NULL DEFAULT '0', + `csvfilename` VARCHAR(255) NOT NULL DEFAULT '', + `enable_scheduled_injection` tinyint NOT NULL DEFAULT 0, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET={$default_charset} COLLATE={$default_collation} ROW_FORMAT=DYNAMIC;"; $DB->queryOrDie($query, $DB->error()); @@ -193,9 +195,25 @@ function plugin_datainjection_install() { default : break; - } + } + + // Register crontask DataInjection - return true; + $cron = new CronTask (); + + if (! $cron->getFromDBbyName ( 'PluginDatainjectionCron', 'DataInjection' )) { + CronTask::Register ( 'PluginDatainjectionCron', 'DataInjection', DAY_TIMESTAMP, // 86400 sec + [ // 'allowmode', 'comment', 'hourmax', 'hourmin', 'logs_lifetime', 'mode', 'param', 'state' + 'state' => CronTask::STATE_DISABLE, + 'comment' => 'File injection from CSV with defined models', + 'mode' => CronTask::MODE_EXTERNAL, + 'hourmin' => '3', + 'hourmax' => '8' + ] ); + } + + + return true; } @@ -220,6 +238,9 @@ function plugin_datainjection_uninstall() { } plugin_init_datainjection(); + + CronTask::Unregister('PluginDatainjectionCron'); + return true; } diff --git a/inc/cron.class.php b/inc/cron.class.php new file mode 100644 index 00000000..94bdaaa5 --- /dev/null +++ b/inc/cron.class.php @@ -0,0 +1,201 @@ +. + * ------------------------------------------------------------------------- + * @copyright Copyright (C) 2007-2022 by DataInjection plugin team. + * @copyright Copyright (C) 2023 Mirko Morandini, Wuerth Phoenix. + * @license GPLv2 https://www.gnu.org/licenses/gpl-2.0.html + * @link https://github.com/pluginsGLPI/datainjection + * ------------------------------------------------------------------------- + */ + + /* + * To add to DB (in hook): + ALTER TABLE glpi_plugin_datainjection_models ADD csvfilename VARCHAR(255) COLLATE utf8_unicode_ci NOT NULL DEFAULT ''; + ALTER TABLE glpi_plugin_datainjection_models ADD enable_scheduled_injection tinyint(1) NOT NULL DEFAULT 0; + */ + +if (! defined ( 'GLPI_ROOT' )) { + die ( "Sorry. You can't access directly to this file" ); +} + +class PluginDatainjectionCron extends CronTask { + + const GLPI_FILEINJECT_DIR = GLPI_VAR_DIR."/_fileinject"; // Path for storage of files to inject + + static public function rawSearchOptionsToAdd($model) { + $tab [] = [ + 'id' => '90', + 'table' => $model->getTable(), + 'field' => 'csvfilename', + 'name' => 'CSV Filename', + 'datatype' => 'text' + ]; + $tab [] = [ + 'id' => '91', + 'table' => $model->getTable(), + 'field' => 'enable_scheduled_injection', + 'name' => 'Enable scheduled injection', + 'datatype' => 'bool' + ]; + return $tab; + } + + function showAdditionnalForm(PluginDatainjectionModel $model) { + + // $id = $this->getFromDBByModelID($model->fields['id']); + // $canedit = $this->can($id, UPDATE); + + echo "".__('Scheduled import (Automatic Action) options', 'datainjection').""; + + echo ""; + echo "".__('Enable scheduled injection', 'datainjection').""; + echo ""; + Dropdown::showYesNo("enable_scheduled_injection", $model->getEnableScheduledInjection()); + echo ""."CSV Filename (in ".self::GLPI_FILEINJECT_DIR.")".""; + echo ""; + echo ""; + echo ""; + } + + + /** + * Give AutomaticAction cron information + * + * @param $name : + * automatic action's name + * + * @return array + */ + static function cronInfo($name) { + switch ($name) { + case 'cronDataInjection' : + return ['description' => __('Scheduled Data Injection', 'datainjection')]; + #case 'cronDataInjectionAdditional' : + # return ['description' => __('Scheduled Data Injection Additional', 'datainjection')]; + } + return [ ]; + } + /** + * The Cron Job + * @param CronTask $item + * @return number + */ + // public static function cronDataInjectionAdditional(CronTask $item) { + // return self::cronDataInjection($item); + // } + + // New job: searches in all models for an enabled scheduling and the filename saved there. + // Models need to be public or by root user! + static function cronDataInjection ($task = NULL) { + global $DB; + + // To prevent problem of execution time during injection + ini_set("max_execution_time", "0"); + + #$models = PluginDatainjectionModel::getModels(1, "name", 1, false); // 1 = user root + $query = "SELECT `id`, `name`, `is_private`, `entities_id`, `is_recursive`, `itemtype`,`step`, `comment` + FROM `glpi_plugin_datainjection_models` WHERE `step` = '".PluginDatainjectionModel::READY_TO_USE_STEP."' AND ("; + $query .= "(`is_private` = '" . PluginDatainjectionModel::MODEL_PUBLIC."') OR (`users_id` = 1)) ORDER BY `name` DESC"; + $models = $DB->request($query); + + if (count($models) == 0) { + $task->log("No valid public or root models found!"); + error_log('[DEBUG] no valid models found.'); + return 0; + } + + $returndata = 0; //0: nothing_to_do, >0: success, <0: partial + // a loop on all models to get the params and execute each one in sequence: + foreach ($models as $singlemodel){ + $model = new PluginDatainjectionModel(); + $filename = ''; + ##$model->can($modelid, READ); + $model->getFromDB($singlemodel['id']); + if (!$model->getEnableScheduledInjection() || empty($model->getCSVFilename())){ + # $task->log("Not to schedule....!"); + continue; + } + $filename = self::GLPI_FILEINJECT_DIR ."/". $model->getCSVFilename(); + $modelname = $singlemodel['name']; + + $task->log("Executing scheduled model $modelname on file $filename."); + if (!file_exists($filename)) { + $task->log("[WARNING] File $filename, defined in $modelname, does not exist. Disable scheduled injection!"); + error_log ("[WARNING] File $filename, defined in $modelname, does not exist. Disable scheduled injection!"); + #$returndata = -1; + continue; + } + + //Read file using automatic encoding detection, and do not delete file once read + $options = array('file_encoding' => PluginDatainjectionBackend::ENCODING_AUTO, + 'mode' => PluginDatainjectionModel::PROCESS, + 'unique_filename' => $filename, + 'original_filename' => $filename, + 'delete_file' => false); + $response = $model->processUploadedFile($options); + + if (! $response) { + $task->log ("[ERROR] import errors for file $filename in model $modelname."); + error_log ("[ERROR] import errors for file $filename in model $modelname."); + $returndata = -1; + continue; + } + + $additional_infos = []; + $engine = new PluginDatainjectionEngine( + $model, + $additional_infos, + $singlemodel['entities_id'] + ); + //Remove first line if header is present + $first = true; + $nb = 0; + foreach ($model->injectionData->getData() as $id => $data) { + if ($first && $model->getSpecificModel()->isHeaderPresent()) { + $first = false; + } else { + $results[] = $engine->injectLine($data[0], $id); + $nb++; + } + } + $model->cleanData(); + + if (! is_null ( $task )) { + // If the argument $task is a CronTask object, the method must increment the quantity of actions done. + $task->addVolume ( 1 ); + $task->log ( "Import/update of $nb rows for model $modelname successful." ); + error_log ( "Import/update of $nb rows for model $modelname successful." ); + if ($returndata == 0) + $returndata = 1; + } + } + + return $returndata; + + } + +} + +// END \ No newline at end of file diff --git a/inc/model.class.php b/inc/model.class.php index 041ff133..6e425b8b 100644 --- a/inc/model.class.php +++ b/inc/model.class.php @@ -225,9 +225,17 @@ function getFloatFormat() { function getPortUnicity() { return $this->fields["port_unicity"]; + } + + function getCSVFilename() { + #remove all "../" to avoid excaping from the standard directory + return trim(str_replace("../","",$this->fields["csvfilename"])); } - - + + function getEnableScheduledInjection() { + return $this->fields["enable_scheduled_injection"]; + } + function getNumberOfMappings() { if ($this->mappings) { @@ -482,6 +490,7 @@ function rawSearchOptions() { 'name' => __('Child entities'), 'datatype' => 'bool', ]; + $tab = array_merge ( $tab, PluginDatainjectionCron::rawSearchOptionsToAdd ($this) ); return $tab; } @@ -701,8 +710,11 @@ function showAdvancedForm($ID, $options = []) { if ($ID > 0) { $tmp = self::getInstance('csv'); $tmp->showAdditionnalForm($this); + + $tmp = new PluginDatainjectionCron(); + $tmp->showAdditionnalForm($this); } - + $this->showFormButtons($options); return true; } @@ -942,8 +954,9 @@ function readUploadedFile($options = []) { //Get model & model specific fields $this->loadSpecificModel(); - if (!$webservice) { - //Get and store uploaded file + // load local file by cron + if (!$webservice && !$unique_filename) { + //Get and store uploaded file $original_filename = $_FILES['filename']['name']; $temporary_uploaded_filename = $_FILES["filename"]["tmp_name"]; $unique_filename = tempnam(realpath(PLUGIN_DATAINJECTION_UPLOAD_DIR), "PWS"); From e243575da1aeefd6df4f5b646b05fdb9758fa05c Mon Sep 17 00:00:00 2001 From: Mirkk Date: Thu, 15 Feb 2024 18:02:55 +0100 Subject: [PATCH 3/3] remove the limit of number of lines read by the plugin in the CSV file thanks to @ready2play32 --- inc/model.class.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inc/model.class.php b/inc/model.class.php index 6e425b8b..9fef8c77 100644 --- a/inc/model.class.php +++ b/inc/model.class.php @@ -991,7 +991,7 @@ function readUploadedFile($options = []) { if (!$webservice) { //Read n line from the CSV file if not webservice - $injectionData = $backend->read(20); + $injectionData = $backend->read(-1); } else { //Read the whole file $injectionData = $backend->read(-1);