diff --git a/ajax/inject_batch.php b/ajax/inject_batch.php new file mode 100644 index 00000000..2096cf47 --- /dev/null +++ b/ajax/inject_batch.php @@ -0,0 +1,42 @@ +. + * ------------------------------------------------------------------------- + * @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 + * ------------------------------------------------------------------------- + */ +use function Safe\json_encode; + +header("Content-Type: application/json; charset=UTF-8"); +Html::header_nocache(); + +Session::checkCentralAccess(); + +$offset = (int) ($_POST['offset'] ?? 0); +$batch_size = (int) ($_POST['batch_size'] ?? 10); + +echo json_encode( + PluginDatainjectionClientInjection::processBatch($offset, $batch_size), +); diff --git a/inc/clientinjection.class.php b/inc/clientinjection.class.php index e9060e53..66b4e210 100644 --- a/inc/clientinjection.class.php +++ b/inc/clientinjection.class.php @@ -42,34 +42,6 @@ use function Safe\readfile; use function Safe\unlink; -/** - * ------------------------------------------------------------------------- - * DataInjection plugin for GLPI - * ------------------------------------------------------------------------- - * - * LICENSE - * - * This file is part of DataInjection. - * - * DataInjection is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * DataInjection is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with DataInjection. If not, see . - * ------------------------------------------------------------------------- - * @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 - * ------------------------------------------------------------------------- - */ - class PluginDatainjectionClientInjection { public static $rightname = "plugin_datainjection_use"; @@ -78,9 +50,6 @@ class PluginDatainjectionClientInjection public const STEP_PROCESS = 1; public const STEP_RESULT = 2; - //Injection results - private $results = []; - /** * Print a good title for group pages * @@ -158,117 +127,133 @@ public static function showUploadFileForm($options = []) **/ public static function showInjectionForm(PluginDatainjectionModel $model, $entities_id) { + /** @var array $CFG_GLPI */ + global $CFG_GLPI; + if (!PluginDatainjectionSession::getParam('infos')) { PluginDatainjectionSession::setParam('infos', []); } + $nblines = PluginDatainjectionSession::getParam('nblines'); + + //Read all CSV lines into session for batch processing + $backend = $model->getBackend(); + $model->loadSpecificModel(); + $backend->openFile(); + + $lines = []; + $line = $backend->getNextLine(); + + //If header is present, skip it + if ($model->getSpecificModel()->isHeaderPresent()) { + $line = $backend->getNextLine(); + } + + while ($line != null) { + $lines[] = $line; + $line = $backend->getNextLine(); + } + $backend->closeFile(); + + //Store lines in session for batch processing + PluginDatainjectionSession::setParam('injection_lines', json_encode($lines)); + PluginDatainjectionSession::setParam('injection_results', json_encode([])); + PluginDatainjectionSession::setParam('injection_error_lines', json_encode([])); + + $batch_url = $CFG_GLPI['root_doc'] . "/plugins/datainjection/ajax/inject_batch.php"; + $result_url = $CFG_GLPI['root_doc'] . "/plugins/datainjection/ajax/results.php"; + TemplateRenderer::getInstance()->display('@datainjection/clientinjection_injection.html.twig', [ 'model_name' => $model->fields['name'], + 'nblines' => $nblines, + 'model_id' => $model->fields['id'], + 'batch_url' => $batch_url, + 'result_url' => $result_url, + 'plugin_url' => plugin_datainjection_geturl(), ]); - // L'injection réelle reste côté PHP, mais tu peux déclencher l'appel Ajax ici si besoin echo ""; - self::processInjection($model, $entities_id); } /** - * @param PluginDatainjectionModel $model - * @param integer $entities_id + * Process a batch of injection lines. + * + * @param int $offset Starting line offset + * @param int $batch_size Number of lines to process in this batch + * + * @return array JSON-serializable result with progress info **/ - public static function processInjection(PluginDatainjectionModel $model, $entities_id) + public static function processBatch(int $offset, int $batch_size): array { - /** @var array $CFG_GLPI */ - global $CFG_GLPI; - try { ini_set("max_execution_time", "0"); } catch (InfoException $e) { - //empty catch -- but keep trace of issue ErrorHandler::logCaughtException($e); } - // Disable recording each SQL request in $_SESSION Profile::getCurrent()->disable(); - $nblines = PluginDatainjectionSession::getParam('nblines'); - $clientinjection = new PluginDatainjectionClientInjection(); + $model = unserialize($_SESSION['datainjection']['currentmodel']); + $model->loadSpecificModel(); + $entities_id = $_SESSION['glpiactive_entity']; + $lines_json = PluginDatainjectionSession::getParam('injection_lines'); + $lines = json_decode($lines_json, true); + + $results_json = PluginDatainjectionSession::getParam('injection_results'); + $results = json_decode($results_json, true) ?: []; + $error_lines_json = PluginDatainjectionSession::getParam('injection_error_lines'); + $error_lines = json_decode($error_lines_json, true) ?: []; - //New injection engine $engine = new PluginDatainjectionEngine( $model, PluginDatainjectionSession::getParam('infos'), $entities_id, ); - $backend = $model->getBackend(); - $model->loadSpecificModel(); - //Open CSV file - $backend->openFile(); + $header_offset = $model->getSpecificModel()->isHeaderPresent() ? 2 : 1; + $total = count($lines); + $end = min($offset + $batch_size, $total); - $index = 0; - - //Read CSV file - $line = $backend->getNextLine(); - - //If header is present, then get the second line - if ($model->getSpecificModel()->isHeaderPresent()) { - $line = $backend->getNextLine(); - } + for ($i = $offset; $i < $end; $i++) { + $injectionline = $i + $header_offset; + $result = $engine->injectLine($lines[$i][0], $injectionline); + $results[] = $result; - //While CSV file is not EOF - $prev = ''; - $deb = time(); - while ($line != null) { - //Inject line - $injectionline = $index + ($model->getSpecificModel()->isHeaderPresent() ? 2 : 1); - $clientinjection->results[] = $engine->injectLine($line[0], $injectionline); - - $pos = number_format($index * 100 / $nblines, 1); - if ($pos != $prev) { - $prev = $pos; - $fin = time() - $deb; + if ($result['status'] != PluginDatainjectionCommonInjectionLib::SUCCESS) { + $error_lines[] = $lines[$i][0]; } - $line = $backend->getNextLine(); - $index++; } - $js = <<enable(); + //Store updated results + PluginDatainjectionSession::setParam('injection_results', json_encode($results)); + PluginDatainjectionSession::setParam('injection_error_lines', json_encode($error_lines)); - //Close CSV file - $backend->closeFile(); + $done = ($end >= $total); + $progress = $total > 0 ? round(($end / $total) * 100, 1) : 100; - //Delete CSV file - $backend->deleteFile(); + if ($done) { + //Finalize: move results to the standard session params, clean up + PluginDatainjectionSession::setParam('results', json_encode($results)); + PluginDatainjectionSession::setParam('error_lines', json_encode($error_lines)); - //Change step - $_SESSION['datainjection']['step'] = self::STEP_RESULT; + $_SESSION['datainjection']['step'] = self::STEP_RESULT; + unset($_SESSION['datainjection']['go']); - //Display results form - PluginDatainjectionSession::setParam('results', json_encode($clientinjection->results)); - PluginDatainjectionSession::setParam('error_lines', json_encode($engine->getLinesInError())); - $p['models_id'] = $model->fields['id']; - $p['nblines'] = $nblines; + //Delete CSV file + $backend = $model->getBackend(); + $backend->deleteFile(); + } - unset($_SESSION['datainjection']['go']); + Profile::getCurrent()->enable(); - $url = $CFG_GLPI['root_doc'] . "/plugins/datainjection/ajax/results.php"; - Ajax::updateItem("span_injection", $url, $p); + return [ + 'progress' => $progress, + 'done' => $done, + 'offset' => $end, + 'total' => $total, + 'processed' => $end, + ]; } diff --git a/inc/session.class.php b/inc/session.class.php index 261dd9ff..8a5bd2dd 100644 --- a/inc/session.class.php +++ b/inc/session.class.php @@ -47,7 +47,7 @@ public static function getParam($param) if (!isset($_SESSION['datainjection'][$param])) { return false; } - if (in_array($param, ['results', 'error_lines'])) { + if (in_array($param, ['results', 'error_lines', 'injection_lines', 'injection_results', 'injection_error_lines'])) { $fic = $_SESSION['datainjection'][$param]; return file_get_contents(GLPI_TMP_DIR . '/' . $fic); } @@ -66,7 +66,13 @@ public static function getParam($param) public static function setParam($param, $results): void { - if (in_array($param, ['results', 'error_lines'])) { + if (in_array($param, ['results', 'error_lines', 'injection_lines', 'injection_results', 'injection_error_lines'])) { + if (isset($_SESSION['datainjection'][$param])) { + $old_fic = GLPI_TMP_DIR . '/' . $_SESSION['datainjection'][$param]; + if (file_exists($old_fic)) { + unlink($old_fic); + } + } $fic = Session::getLoginUserID() . '_' . $param . '_' . microtime(true); file_put_contents(GLPI_TMP_DIR . '/' . $fic, $results); $_SESSION['datainjection'][$param] = $fic; @@ -84,11 +90,14 @@ public static function setParam($param, $results): void public static function removeParams(): void { - if (isset($_SESSION['datainjection']['results'])) { - unlink(GLPI_TMP_DIR . '/' . $_SESSION['datainjection']['results']); - } - if (isset($_SESSION['datainjection']['error_lines'])) { - unlink(GLPI_TMP_DIR . '/' . $_SESSION['datainjection']['error_lines']); + $file_params = ['results', 'error_lines', 'injection_lines', 'injection_results', 'injection_error_lines']; + foreach ($file_params as $param) { + if (isset($_SESSION['datainjection'][$param])) { + $fic = GLPI_TMP_DIR . '/' . $_SESSION['datainjection'][$param]; + if (file_exists($fic)) { + unlink($fic); + } + } } unset($_SESSION['datainjection']); } diff --git a/templates/clientinjection_injection.html.twig b/templates/clientinjection_injection.html.twig index ec222295..e230d433 100644 --- a/templates/clientinjection_injection.html.twig +++ b/templates/clientinjection_injection.html.twig @@ -34,13 +34,33 @@ - + {{ __('Import progress', 'datainjection') }} - - - {{ __('Injection of the file', 'datainjection') }} + + + 0% - \ No newline at end of file + + + {{ __('Injection of the file', 'datainjection') }} — 0 / {{ nblines }} {{ __(' lines', 'datainjection') }} + + + + \ No newline at end of file