Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
- Restored group selection in mappings
- Fix special caracters malformed in translations
- Fix error when displaying the additional information form
- Fix injection loading bar
Comment thread
Rom1-B marked this conversation as resolved.
Outdated
- Fix the `computer contact` injection when the corresponding value in the CSV file is empty

## [2.15.3] - 2025-12-22
Expand Down
42 changes: 42 additions & 0 deletions ajax/inject_batch.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<?php

/**
* -------------------------------------------------------------------------
* 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 <http://www.gnu.org/licenses/>.
* -------------------------------------------------------------------------
* @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),
);
187 changes: 86 additions & 101 deletions inc/clientinjection.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -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 <http://www.gnu.org/licenses/>.
* -------------------------------------------------------------------------
* @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";
Expand All @@ -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
*
Expand Down Expand Up @@ -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 "<span id='span_injection' name='span_injection'></span>";
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 = <<<JAVASCRIPT
$(function() {
const progress = document.querySelector('.progress');
const progressBar = document.querySelector('.progress-bar');
if (progressBar && progress) {
progressBar.style.width = '100%';
progress.setAttribute('aria-valuenow', '100');
}
});
JAVASCRIPT;

//EOF : change progressbar to 100% !
echo Html::scriptBlock($js);

// Restore
Profile::getCurrent()->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,
];
}


Expand Down
23 changes: 16 additions & 7 deletions inc/session.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand All @@ -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;
Expand All @@ -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']);
}
Expand Down
30 changes: 25 additions & 5 deletions templates/clientinjection_injection.html.twig
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,33 @@
</span>
</h3>
</div>
<div class="card p-3 my-5">
<div class="card p-3 my-5"
id="injection_container"
data-batch-url="{{ batch_url }}"
data-result-url="{{ result_url }}"
data-model-id="{{ model_id }}"
data-nblines="{{ nblines }}"
data-batch-size="10"
data-status-label="{{ __('Injection of the file', 'datainjection') }}"
data-lines-label="{{ __(' lines', 'datainjection') }}"
data-error-label="{{ __('Error', 'datainjection') }}"
>
<div class="hr-text mt-5 mb-5">
<i class="fa-2x ti ti-chart-bar"></i>
<span>{{ __('Import progress', 'datainjection') }}</span>
</div>
<div class="progress" role="progressbar" aria-label={{ __('Injection of the file', 'datainjection') }} aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" style="height: 20px;">
<div class="progress-bar progress-bar-striped bg-info" style="width: 0%; height: 20px;">
{{ __('Injection of the file', 'datainjection') }}
<div class="progress" role="progressbar" aria-label="{{ __('Injection of the file', 'datainjection') }}" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" style="height: 20px;">
<div class="progress-bar progress-bar-striped progress-bar-animated bg-info" style="width: 0%; height: 20px;">
0%
</div>
</div>
</div>
<div class="text-muted text-center mt-2" id="injection_status">
{{ __('Injection of the file', 'datainjection') }} — 0 / {{ nblines }} {{ __(' lines', 'datainjection') }}
</div>

<script src="{{ plugin_url }}js/injection_progress.js"></script>
<script>
$(function() {
startBatchInjection(document.getElementById('injection_container'));
});
</script>