Files
MYSOPHAL/inc/ruledictionnarysoftwarecollection.class.php
2025-08-07 13:15:31 +01:00

586 lines
20 KiB
PHP

<?php
/**
* ---------------------------------------------------------------------
* GLPI - Gestionnaire Libre de Parc Informatique
* Copyright (C) 2015-2020 Teclib' and contributors.
*
* http://glpi-project.org
*
* based on GLPI - Gestionnaire Libre de Parc Informatique
* Copyright (C) 2003-2014 by the INDEPNET Development Team.
*
* ---------------------------------------------------------------------
*
* LICENSE
*
* This file is part of GLPI.
*
* GLPI 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.
*
* GLPI 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 GLPI. If not, see <http://www.gnu.org/licenses/>.
* ---------------------------------------------------------------------
*/
if (!defined('GLPI_ROOT')) {
die("Sorry. You can't access this file directly");
}
class RuleDictionnarySoftwareCollection extends RuleCollection {
// From RuleCollection
public $stop_on_first_match = true;
public $can_replay_rules = true;
public $menu_type = 'dictionnary';
public $menu_option = 'software';
static $rightname = 'rule_dictionnary_software';
/**
* @see RuleCollection::getTitle()
**/
function getTitle() {
//TRANS: software in plural
return __('Dictionnary of software');
}
/**
* @see RuleCollection::cleanTestOutputCriterias()
**/
function cleanTestOutputCriterias(array $output) {
//If output array contains keys begining with _ : drop it
foreach ($output as $criteria => $value) {
if (($criteria[0] == '_') && ($criteria != '_ignore_import')) {
unset ($output[$criteria]);
}
}
return $output;
}
/**
* @see RuleCollection::warningBeforeReplayRulesOnExistingDB()
**/
function warningBeforeReplayRulesOnExistingDB($target) {
global $CFG_GLPI;
echo "<form name='testrule_form' id='softdictionnary_confirmation' method='post' action=\"" .
$target . "\">\n";
echo "<div class='center'>";
echo "<table class='tab_cadre_fixe'>";
echo "<tr><th colspan='2' class='b'>" .
__('Warning before running rename based on the dictionary rules') . "</th></tr>\n";
echo "<tr><td class='tab_bg_2 center'>";
echo "<img src=\"" . $CFG_GLPI["root_doc"] . "/pics/warning.png\"></td>";
echo "<td class='tab_bg_2 center'>" .
__('Warning! This operation can put merged software in the trashbin.<br>Sure to notify your users.').
"</td></tr>\n";
echo "<tr><th colspan='2' class='b'>" . __('Manufacturer choice') . "</th></tr>\n";
echo "<tr><td class='tab_bg_2 center'>" .
__('Replay dictionary rules for manufacturers (----- = All)') . "</td>";
echo "<td class='tab_bg_2 center'>";
Manufacturer::dropdown(['name' => 'manufacturer']);
echo "</td></tr>\n";
echo "<tr><td class='tab_bg_2 center' colspan='2'>";
echo "<input type='submit' name='replay_rule' value=\""._sx('button', 'Post')."\"
class='submit'>";
echo "<input type='hidden' name='replay_confirm' value='replay_confirm'>";
echo "</td></tr>";
echo "</table>\n";
echo "</div>\n";
Html::closeForm();
return true;
}
/**
* @see RuleCollection::replayRulesOnExistingDB()
**/
function replayRulesOnExistingDB($offset = 0, $maxtime = 0, $items = [], $params = []) {
global $DB;
if (isCommandLine()) {
echo "replayRulesOnExistingDB started : " . date("r") . "\n";
}
$nb = 0;
$i = $offset;
if (count($items) == 0) {
//Select all the differents software
$criteria = [
'SELECT' => [
'glpi_softwares.name',
'glpi_manufacturers.name AS manufacturer',
'glpi_softwares.manufacturers_id AS manufacturers_id',
'glpi_softwares.entities_id AS entities_id',
'glpi_softwares.is_helpdesk_visible AS helpdesk',
'glpi_softwares.softwarecategories_id AS softwarecategories_id',
],
'DISTINCT' => true,
'FROM' => 'glpi_softwares',
'LEFT JOIN' => [
'glpi_manufacturers' => [
'ON' => [
'glpi_manufacturers' => 'id',
'glpi_softwares' => 'manufacturers_id'
]
]
],
'WHERE' => [
// Do not replay on trashbin and templates
'glpi_softwares.is_deleted' => 0,
'glpi_softwares.is_template' => 0
]
];
if (isset($params['manufacturer']) && $params['manufacturer']) {
$criteria['WHERE']['glpi_softwares.manufacturers_id'] = $params['manufacturer'];
}
if ($offset) {
$criteria['START'] = (int)$offset;
}
$iterator = $DB->request($criteria);
$nb = count($iterator) + $offset;
$step = (($nb > 1000) ? 50 : (($nb > 20) ? floor(count($iterator) / 20) : 1));
while ($input = $iterator->next()) {
if (!($i % $step)) {
if (isCommandLine()) {
printf(__('%1$s - replay rules on existing database: %2$s/%3$s (%4$s Mio)')."\n",
date("H:i:s"), $i, $nb, round(memory_get_usage() / (1024 * 1024), 2));
} else {
Html::changeProgressBarPosition($i, $nb, "$i / $nb");
}
}
//If manufacturer is set, then first run the manufacturer's dictionnary
if (isset($input["manufacturer"])) {
$input["manufacturer"] = Manufacturer::processName($input["manufacturer"]);
}
//Replay software dictionnary rules
$res_rule = $this->processAllRules($input, [], []);
if ((isset($res_rule["name"]) && (strtolower($res_rule["name"]) != strtolower($input["name"])))
|| (isset($res_rule["version"]) && ($res_rule["version"] != ''))
|| (isset($res_rule['new_entities_id'])
&& ($res_rule['new_entities_id'] != $input['entities_id']))
|| (isset($res_rule['is_helpdesk_visible'])
&& ($res_rule['is_helpdesk_visible'] != $input['helpdesk']))
|| (isset($res_rule['manufacturer'])
&& ($res_rule['manufacturer'] != $input['manufacturer']))
|| (isset($res_rule['softwarecategories_id'])
&& ($res_rule['softwarecategories_id'] != $input['softwarecategories_id']))) {
$IDs = [];
//Find all the softwares in the database with the same name and manufacturer
$same_iterator = $DB->request([
'SELECT' => 'id',
'FROM' => 'glpi_softwares',
'WHERE' => [
'name' => $input['name'],
'manufacturers_id' => $input['manufacturers_id']
]
]);
if (count($same_iterator)) {
//Store all the software's IDs in an array
while ($result = $same_iterator->next()) {
$IDs[] = $result["id"];
}
//Replay dictionnary on all the softwares
$this->replayDictionnaryOnSoftwaresByID($IDs, $res_rule);
}
}
$i++;
if ($maxtime) {
$crt = explode(" ", microtime());
if (($crt[0] + $crt[1]) > $maxtime) {
break;
}
}
} // each distinct software
if (isCommandLine()) {
printf(__('Replay rules on existing database: %1$s/%2$s')." \n", $i, $nb);
} else {
Html::changeProgressBarPosition($i, $nb, "$i / $nb");
}
} else {
$this->replayDictionnaryOnSoftwaresByID($items);
return true;
}
if (isCommandLine()) {
printf(__('Replay rules on existing database ended on %s')."\n", date("r"));
}
return (($i == $nb) ? -1 : $i);
}
/**
* Replay dictionnary on several softwares
*
* @param $IDs array of software IDs to replay
* @param $res_rule array of rule results
*
* @return Query result handler
**/
function replayDictionnaryOnSoftwaresByID(array $IDs, $res_rule = []) {
global $DB;
$new_softs = [];
$delete_ids = [];
foreach ($IDs as $ID) {
$iterator = $DB->request([
'SELECT' => [
'gs.id',
'gs.name AS name',
'gs.entities_id AS entities_id',
'gm.name AS manufacturer'
],
'FROM' => 'glpi_softwares AS gs',
'LEFT JOIN' => [
'glpi_manufacturers AS gm' => [
'ON' => [
'gs' => 'manufacturers_id',
'gm' => 'id'
]
]
],
'WHERE' => [
'gs.is_template' => 0,
'gs.id' => $ID
]
]);
if (count($iterator)) {
$soft = $iterator->next();
//For each software
$this->replayDictionnaryOnOneSoftware(
$new_softs,
$res_rule,
$ID,
(isset($res_rule['new_entities_id'])
? $res_rule['new_entities_id']
: $soft["entities_id"]
),
$soft['name'] ?? '',
$soft['manufacturer'] ?? '',
$delete_ids
);
}
}
//Delete software if needed
$this->putOldSoftsInTrash($delete_ids);
}
/**
* Replay dictionnary on one software
*
* @param &$new_softs array containing new softwares already computed
* @param $res_rule array of rule results
* @param $ID ID of the software
* @param $entity working entity ID
* @param $name softwrae name
* @param $manufacturer manufacturer name
* @param &$soft_ids array containing replay software need to be put in trashbin
**/
function replayDictionnaryOnOneSoftware(array &$new_softs, array $res_rule, $ID, $entity, $name,
$manufacturer, array &$soft_ids) {
global $DB;
$input["name"] = $name;
$input["manufacturer"] = $manufacturer;
$input["entities_id"] = $entity;
if (empty($res_rule)) {
$res_rule = $this->processAllRules($input, [], []);
}
$soft = new Software();
if (isset($res_rules['_ignore_import']) && ($res_rules['_ignore_import'] == 1)) {
$soft->putInTrash($ID, __('Software deleted by GLPI dictionary rules'));
return;
}
//Software's name has changed or entity
if ((isset($res_rule["name"]) && (strtolower($res_rule["name"]) != strtolower($name)))
//Entity has changed, and new entity is a parent of the current one
|| (!isset($res_rule["name"])
&& isset($res_rule['new_entities_id'])
&& in_array($res_rule['new_entities_id'],
getAncestorsOf('glpi_entities', $entity)))) {
if (isset($res_rule["name"])) {
$new_name = $res_rule["name"];
} else {
$new_name = addslashes($name);
}
if (isset($res_rule["manufacturer"]) && $res_rule["manufacturer"]) {
$manufacturer = $res_rule["manufacturer"];
} else {
$manufacturer = addslashes($manufacturer);
}
//New software not already present in this entity
if (!isset($new_softs[$entity][$new_name])) {
// create new software or restore it from trashbin
$new_software_id = $soft->addOrRestoreFromTrash($new_name, $manufacturer,
$entity, '', true);
$new_softs[$entity][$new_name] = $new_software_id;
} else {
$new_software_id = $new_softs[$entity][$new_name];
}
// Move licenses to new software
$this->moveLicenses($ID, $new_software_id);
} else {
$new_software_id = $ID;
$res_rule["id"] = $ID;
if (isset($res_rule["manufacturer"]) && $res_rule["manufacturer"]) {
$res_rule["manufacturers_id"] = Dropdown::importExternal('Manufacturer',
$res_rule["manufacturer"]);
unset($res_rule["manufacturer"]);
}
$soft->update($res_rule);
}
// Add to software to deleted list
if ($new_software_id != $ID) {
$soft_ids[] = $ID;
}
//Get all the different versions for a software
$iterator = $DB->request([
'FROM' => 'glpi_softwareversions',
'WHERE' => ['softwares_id' => $ID]
]);
while ($version = $iterator->next()) {
$input["version"] = addslashes($version["name"]);
$old_version_name = $input["version"];
if (isset($res_rule['version_append']) && $res_rule['version_append'] != '') {
$new_version_name = $old_version_name . $res_rule['version_append'];
} else if (isset($res_rule["version"]) && $res_rule["version"] != '') {
$new_version_name = $res_rule["version"];
} else {
$new_version_name = $version["name"];
}
if (($ID != $new_software_id)
|| ($new_version_name != $old_version_name)) {
$this->moveVersions($ID, $new_software_id, $version["id"], $old_version_name,
$new_version_name, $entity);
}
}
}
/**
* Delete a list of softwares
*
* @param $soft_ids array containing replay software need to be put in trashbin
**/
function putOldSoftsInTrash(array $soft_ids) {
global $DB;
if (count($soft_ids) > 0) {
//Try to delete all the software that are not used anymore
// (which means that don't have version associated anymore)
$iterator = $DB->request([
'SELECT' => [
'glpi_softwares.id',
'COUNT' => 'glpi_softwareversions.softwares_id AS cpt'
],
'FROM' => 'glpi_softwares',
'LEFT JOIN' => [
'glpi_softwareversions' => [
'ON' => [
'glpi_softwareversions' => 'softwares_id',
'glpi_softwares' => 'id'
]
]
],
'WHERE' => [
'glpi_softwares.id' => $soft_ids,
'is_deleted' => 0
],
'GROUPBY' => 'glpi_softwares.id',
'HAVING' => ['cpt' => 0]
]);
$software = new Software();
while ($soft = $iterator->next()) {
$software->putInTrash($soft["id"], __('Software deleted by GLPI dictionary rules'));
}
}
}
/**
* Change software's name, and move versions if needed
*
* @param int $ID old software ID
* @param int $new_software_id new software ID
* @param int $version_id version ID to move
* @param string $old_version old version name
* @param string $new_version new version name
* @param int $entity entity ID
* @return void
*/
function moveVersions($ID, $new_software_id, $version_id, $old_version, $new_version, $entity) {
global $DB;
$new_versionID = $this->versionExists($new_software_id, $new_version);
// Do something if it is not the same version
if ($new_versionID != $version_id) {
//A version does not exist : update existing one
if ($new_versionID == -1) {
//Transfer versions from old software to new software for a specific version
$DB->update(
'glpi_softwareversions', [
'name' => $new_version,
'softwares_id' => $new_software_id
], [
'id' => $version_id
]
);
} else {
// Delete software can be in double after update
$item_softwareversion_table = Item_SoftwareVersion::getTable();
$iterator = $DB->request([
'SELECT' => ['gcs_2.*'],
'FROM' => $item_softwareversion_table,
'LEFT JOIN' => [
"{$item_softwareversion_table} AS gcs_2" => [
'FKEY' => [
'gcs_2' => 'items_id',
$item_softwareversion_table => 'items_id', [
'AND' => [
'gcs_2.itemtype' => $item_softwareversion_table.'.itemtype'
]
]
]
]
],
'WHERE' => [
"{$item_softwareversion_table}.softwareversions_id" => $new_versionID,
'gcs_2.softwareversions_id' => $version_id
]
]);
while ($data = $iterator->next()) {
$DB->delete(
'glpi_items_softwareversions', [
'id' => $data['id']
]
);
}
//Change ID of the version in glpi_items_softwareversions
$DB->update(
$item_softwareversion_table, [
'softwareversions_id' => $new_versionID
], [
'softwareversions_id' => $version_id
]
);
// Update licenses version link
$DB->update(
'glpi_softwarelicenses', [
'softwareversions_id_buy' => $new_versionID
], [
'softwareversions_id_buy' => $version_id
]
);
$DB->update(
'glpi_softwarelicenses', [
'softwareversions_id_use' => $new_versionID
], [
'softwareversions_id_use' => $version_id
]
);
//Delete old version
$old_version = new SoftwareVersion();
$old_version->delete(["id" => $version_id]);
}
}
}
/**
* Move licenses from a software to another
*
* @param $old_software_id old software ID
* @param $new_software_id new software ID
* @return true if move was successful
**/
function moveLicenses($old_software_id, $new_software_id) {
global $DB;
//Return false if one of the 2 softwares doesn't exists
if (!countElementsInTable('glpi_softwares', ['id' => $old_software_id])
|| !countElementsInTable('glpi_softwares', ['id' => $new_software_id])) {
return false;
}
//Transfer licenses to new software if needed
if ($old_software_id != $new_software_id) {
$DB->update(
'glpi_softwarelicenses', [
'softwares_id' => $new_software_id
], [
'softwares_id' => $old_software_id
]
);
}
return true;
}
/**
* Check if a version exists
*
* @param $software_id software ID
* @param $version version name
**/
function versionExists($software_id, $version) {
global $DB;
//Check if the version exists
$iterator = $DB->request([
'FROM' => 'glpi_softwareversions',
'WHERE' => [
'softwares_id' => $software_id,
'name' => $version
]
]);
if (count($iterator)) {
$current = $iterator->next();
return $current['id'];
}
return -1;
}
}