%PDF- %PDF-
Direktori : /var/www/projetos/suporte.iigd.com.br/src/ |
Current File : /var/www/projetos/suporte.iigd.com.br/src/Log.php |
<?php /** * --------------------------------------------------------------------- * * GLPI - Gestionnaire Libre de Parc Informatique * * http://glpi-project.org * * @copyright 2015-2024 Teclib' and contributors. * @copyright 2003-2014 by the INDEPNET Development Team. * @licence https://www.gnu.org/licenses/gpl-3.0.html * * --------------------------------------------------------------------- * * LICENSE * * This file is part of GLPI. * * This program 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 3 of the License, or * (at your option) any later version. * * This program 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 this program. If not, see <https://www.gnu.org/licenses/>. * * --------------------------------------------------------------------- */ use Glpi\Application\View\TemplateRenderer; use Glpi\Toolbox\Sanitizer; /** * Log Class **/ class Log extends CommonDBTM { const HISTORY_ADD_DEVICE = 1; const HISTORY_UPDATE_DEVICE = 2; const HISTORY_DELETE_DEVICE = 3; const HISTORY_INSTALL_SOFTWARE = 4; const HISTORY_UNINSTALL_SOFTWARE = 5; const HISTORY_DISCONNECT_DEVICE = 6; const HISTORY_CONNECT_DEVICE = 7; const HISTORY_LOCK_DEVICE = 8; const HISTORY_UNLOCK_DEVICE = 9; const HISTORY_LOG_SIMPLE_MESSAGE = 12; const HISTORY_DELETE_ITEM = 13; const HISTORY_RESTORE_ITEM = 14; const HISTORY_ADD_RELATION = 15; const HISTORY_DEL_RELATION = 16; const HISTORY_ADD_SUBITEM = 17; const HISTORY_UPDATE_SUBITEM = 18; const HISTORY_DELETE_SUBITEM = 19; const HISTORY_CREATE_ITEM = 20; const HISTORY_UPDATE_RELATION = 21; const HISTORY_LOCK_RELATION = 22; const HISTORY_LOCK_SUBITEM = 23; const HISTORY_UNLOCK_RELATION = 24; const HISTORY_UNLOCK_SUBITEM = 25; const HISTORY_LOCK_ITEM = 26; const HISTORY_UNLOCK_ITEM = 27; // Plugin must use value starting from const HISTORY_PLUGIN = 1000; public static $rightname = 'logs'; /** @var array */ public static array $queue = []; /** @var bool */ public static bool $use_queue = false; public static function getTypeName($nb = 0) { return __('Historical'); } public function getTabNameForItem(CommonGLPI $item, $withtemplate = 0) { if (!self::canView()) { return ''; } $nb = 0; if ($_SESSION['glpishow_count_on_tabs']) { $nb = countElementsInTable( 'glpi_logs', ['itemtype' => $item->getType(), 'items_id' => $item->getID() ] ); } return self::createTabEntry(self::getTypeName(1), $nb); } public static function displayTabContentForItem(CommonGLPI $item, $tabnum = 1, $withtemplate = 0) { self::showForItem($item); return true; } /** * Construct history for an item * * @param $item CommonDBTM object * @param $oldvalues array of old values updated * @param $values array of all values of the item * * @return boolean for success (at least 1 log entry added) **/ public static function constructHistory(CommonDBTM $item, $oldvalues, $values) { if (!count($oldvalues)) { return false; } // needed to have $SEARCHOPTION list($real_type, $real_id) = $item->getLogTypeID(); $searchopt = Search::getOptions($real_type); if (!is_array($searchopt)) { return false; } $result = 0; foreach ($oldvalues as $key => $oldval) { if (in_array($key, $item->getNonLoggedFields())) { continue; } $changes = []; // Parsing $SEARCHOPTION to find changed field foreach ($searchopt as $key2 => $val2) { if (!isset($val2['table'])) { // skip sub-title continue; } // specific for profile if ( ($item->getType() == 'ProfileRight') && ($key == 'rights') ) { if ( isset($val2['rightname']) && ($val2['rightname'] == $item->fields['name']) ) { $id_search_option = $key2; $changes = [$id_search_option, addslashes($oldval ?? ''), $values[$key] ?? '']; } } else if ( ($val2['linkfield'] == $key && $real_type === $item->getType()) || ($key == $val2['field'] && $val2['table'] == $item->getTable()) || ($val2['linkfield'] == $key && $item->getType() == 'Infocom') ) { // Linkfield or standard field not massive action enable $id_search_option = $key2; // Give ID of the $SEARCHOPTION if ($val2['table'] == $item->getTable()) { if ($val2['field'] === 'completename') { $oldval = CommonTreeDropdown::sanitizeSeparatorInCompletename($oldval); $values[$key] = CommonTreeDropdown::sanitizeSeparatorInCompletename($values[$key]); } $changes = [$id_search_option, addslashes($oldval ?? ''), $values[$key] ?? '']; } else { // other cases; link field -> get data from dropdown if ($val2["table"] != 'glpi_auth_tables') { $changes = [$id_search_option, addslashes(sprintf( __('%1$s (%2$s)'), Dropdown::getDropdownName( $val2["table"], $oldval ), $oldval )), addslashes(sprintf( __('%1$s (%2$s)'), Dropdown::getDropdownName( $val2["table"], $values[$key] ), $values[$key] )) ]; } } break; } } if (count($changes)) { $result = self::history($real_id, $real_type, $changes); } } return $result; } /** * Log history * * @param $items_id * @param $itemtype * @param $changes * @param $itemtype_link (default '') * @param $linked_action (default '0') * * @return boolean success **/ public static function history($items_id, $itemtype, $changes, $itemtype_link = '', $linked_action = '0') { /** @var \DBmysql $DB */ global $DB; $date_mod = $_SESSION["glpi_currenttime"]; if (empty($changes)) { return false; } // create a query to insert history $id_search_option = $changes[0]; $old_value = $changes[1]; $new_value = $changes[2]; if ($uid = Session::getLoginUserID(false)) { if (is_numeric($uid)) { $username = User::getNameForLog($uid); } else { // For cron management $username = $uid; } } else { $username = ""; } if (Session::isImpersonateActive()) { $impersonator_id = Session::getImpersonatorId(); $username = sprintf( __('%1$s impersonated by %2$s'), $username, User::getNameForLog($impersonator_id) ); } $old_value = $DB->escape(Toolbox::substr(stripslashes($old_value), 0, 180)); $new_value = $DB->escape(Toolbox::substr(stripslashes($new_value), 0, 180)); // Security to be sure that values do not pass over the max length if (Toolbox::strlen($old_value) > 255) { $old_value = Toolbox::substr($old_value, 0, 250); } if (Toolbox::strlen($new_value) > 255) { $new_value = Toolbox::substr($new_value, 0, 250); } $params = [ 'items_id' => $items_id, 'itemtype' => $itemtype, 'itemtype_link' => $itemtype_link, 'linked_action' => $linked_action, 'user_name' => addslashes($username), 'date_mod' => $date_mod, 'id_search_option' => $id_search_option, 'old_value' => $old_value, 'new_value' => $new_value ]; if (static::$use_queue) { //use queue rather than direct insert static::$queue[] = $params; return true; } $result = $DB->insert(self::getTable(), $params); if ($result && $DB->affectedRows($result) > 0) { return $_SESSION['glpi_maxhistory'] = $DB->insertId(); } return false; } /** * Show History of an item * * @param $item CommonDBTM object * @param $withtemplate integer withtemplate param (default 0) * **/ public static function showForItem(CommonDBTM $item, $withtemplate = 0) { /** @var array $CFG_GLPI */ global $CFG_GLPI; if (!self::canView()) { return; } $itemtype = $item->getType(); $items_id = $item->getField('id'); $start = intval(($_GET["start"] ?? 0)); $filters = $_GET['filters'] ?? []; $is_filtered = count($filters) > 0; $sql_filters = self::convertFiltersValuesToSqlCriteria($filters); // Total Number of events $total_number = countElementsInTable("glpi_logs", ['items_id' => $items_id, 'itemtype' => $itemtype ]); $filtered_number = countElementsInTable("glpi_logs", ['items_id' => $items_id, 'itemtype' => $itemtype ] + $sql_filters); TemplateRenderer::getInstance()->display('components/logs.html.twig', [ 'total_number' => $total_number, 'filtered_number' => $filtered_number, 'logs' => $filtered_number > 0 ? self::getHistoryData($item, $start, $_SESSION['glpilist_limit'], $sql_filters) : [], 'start' => $start, 'href' => $item::getFormURLWithID($items_id), 'additional_params' => $is_filtered ? http_build_query(['filters' => $filters]) : "", 'is_tab' => true, 'items_id' => $items_id, 'filters' => Sanitizer::dbEscapeRecursive($filters), 'user_names' => $is_filtered ? Log::getDistinctUserNamesValuesInItemLog($item) : [], 'affected_fields' => $is_filtered ? Log::getDistinctAffectedFieldValuesInItemLog($item) : [], 'linked_actions' => $is_filtered ? Log::getDistinctLinkedActionValuesInItemLog($item) : [], 'csv_url' => $CFG_GLPI['root_doc'] . "/front/log/export.php?" . http_build_query([ 'filter' => $filters, 'itemtype' => $item::getType(), 'id' => $item->getId() ]), ]); ; } /** * Retrieve last history Data for an item * * @param CommonDBTM $item Object instance * @param integer $start First line to retrieve (default 0) * @param integer $limit Max number of line to retrieve (0 for all) (default 0) * @param array $sqlfilters SQL filters applied to history (default []) * * @return array of localized log entry (TEXT only, no HTML) **/ public static function getHistoryData(CommonDBTM $item, $start = 0, $limit = 0, array $sqlfilters = []) { $DBread = DBConnection::getReadConnection(); $itemtype = $item->getType(); $items_id = $item->getField('id'); $itemtable = $item->getTable(); $SEARCHOPTION = Search::getOptions($itemtype); $query = [ 'FROM' => self::getTable(), 'WHERE' => [ 'items_id' => $items_id, 'itemtype' => $itemtype ] + $sqlfilters, 'ORDER' => 'id DESC' ]; if ($limit) { $query['START'] = (int)$start; $query['LIMIT'] = (int)$limit; } $iterator = $DBread->request($query); $changes = []; foreach ($iterator as $data) { $tmp = []; $tmp['display_history'] = true; $tmp['id'] = $data["id"]; $tmp['date_mod'] = Html::convDateTime($data["date_mod"]); $tmp['user_name'] = $data["user_name"]; $tmp['field'] = ""; $tmp['change'] = ""; $tmp['datatype'] = ""; // This is an internal device ? if ($data["linked_action"]) { $action_label = self::getLinkedActionLabel($data["linked_action"]); // Yes it is an internal device switch ($data["linked_action"]) { case self::HISTORY_CREATE_ITEM: case self::HISTORY_DELETE_ITEM: case self::HISTORY_LOCK_ITEM: case self::HISTORY_UNLOCK_ITEM: case self::HISTORY_RESTORE_ITEM: $tmp['change'] = $action_label; break; case self::HISTORY_ADD_DEVICE: $tmp['field'] = NOT_AVAILABLE; if ($item2 = getItemForItemtype($data["itemtype_link"])) { if ($item2 instanceof Item_Devices) { $tmp['field'] = $item2->getDeviceTypeName(1); } else { $tmp['field'] = $item2->getTypeName(1); } } //TRANS: %s is the component name $tmp['change'] = sprintf(__('%1$s: %2$s'), $action_label, $data["new_value"]); break; case self::HISTORY_UPDATE_DEVICE: $tmp['field'] = NOT_AVAILABLE; $linktype_field = explode('#', $data["itemtype_link"]); $linktype = $linktype_field[0]; $field = $linktype_field[1]; $devicetype = $linktype::getDeviceType(); $tmp['field'] = $devicetype; $specif_fields = $linktype::getSpecificities(); if (isset($specif_fields[$field]['short name'])) { $tmp['field'] = $devicetype; $tmp['field'] .= " (" . $specif_fields[$field]['short name'] . ")"; } //TRANS: %1$s is the old_value, %2$s is the new_value $tmp['change'] = sprintf( __('%1$s: %2$s'), sprintf(__('%1$s (%2$s)'), $action_label, $tmp['field']), sprintf(__('%1$s by %2$s'), $data["old_value"], $data[ "new_value"]) ); break; case self::HISTORY_DELETE_DEVICE: $tmp['field'] = NOT_AVAILABLE; if ($item2 = getItemForItemtype($data["itemtype_link"])) { if ($item2 instanceof Item_Devices) { $tmp['field'] = $item2->getDeviceTypeName(1); } else { $tmp['field'] = $item2->getTypeName(1); } } //TRANS: %s is the component name $tmp['change'] = sprintf(__('%1$s: %2$s'), $action_label, $data["old_value"]); break; case self::HISTORY_LOCK_DEVICE: $tmp['field'] = NOT_AVAILABLE; if ($item2 = getItemForItemtype($data["itemtype_link"])) { if ($item2 instanceof Item_Devices) { $tmp['field'] = $item2->getDeviceTypeName(1); } else { $tmp['field'] = $item2->getTypeName(1); } } //TRANS: %s is the component name $tmp['change'] = sprintf(__('%1$s: %2$s'), $action_label, $data["old_value"]); break; case self::HISTORY_UNLOCK_DEVICE: $tmp['field'] = NOT_AVAILABLE; if ($item2 = getItemForItemtype($data["itemtype_link"])) { if ($item2 instanceof Item_Devices) { $tmp['field'] = $item2->getDeviceTypeName(1); } else { $tmp['field'] = $item2->getTypeName(1); } } //TRANS: %s is the component name $tmp['change'] = sprintf(__('%1$s: %2$s'), $action_label, $data["new_value"]); break; case self::HISTORY_INSTALL_SOFTWARE: $tmp['field'] = _n('Software', 'Software', 1); //TRANS: %s is the software name $tmp['change'] = sprintf(__('%1$s: %2$s'), $action_label, $data["new_value"]); break; case self::HISTORY_UNINSTALL_SOFTWARE: $tmp['field'] = _n('Software', 'Software', 1); //TRANS: %s is the software name $tmp['change'] = sprintf(__('%1$s: %2$s'), $action_label, $data["old_value"]); break; case self::HISTORY_DISCONNECT_DEVICE: $tmp['field'] = NOT_AVAILABLE; if ($item2 = getItemForItemtype($data["itemtype_link"])) { if ($item2 instanceof Item_Devices) { $tmp['field'] = $item2->getDeviceTypeName(1); } else { $tmp['field'] = $item2->getTypeName(1); } } //TRANS: %s is the item name $tmp['change'] = sprintf(__('%1$s: %2$s'), $action_label, $data["old_value"]); break; case self::HISTORY_CONNECT_DEVICE: $tmp['field'] = NOT_AVAILABLE; if ($item2 = getItemForItemtype($data["itemtype_link"])) { if ($item2 instanceof Item_Devices) { $tmp['field'] = $item2->getDeviceTypeName(1); } else { $tmp['field'] = $item2->getTypeName(1); } } //TRANS: %s is the item name $tmp['change'] = sprintf(__('%1$s: %2$s'), $action_label, $data["new_value"]); break; case self::HISTORY_LOG_SIMPLE_MESSAGE: $tmp['field'] = ""; $tmp['change'] = $data["new_value"]; break; case self::HISTORY_ADD_RELATION: $tmp['field'] = NOT_AVAILABLE; if ($item2 = getItemForItemtype($data["itemtype_link"])) { $tmp['field'] = $item2->getTypeName(1); } $tmp['change'] = sprintf(__('%1$s: %2$s'), $action_label, $data["new_value"]); if ($data['itemtype'] == 'Ticket') { if ($data['id_search_option']) { // Recent record - see CommonITILObject::getSearchOptionsActors() $as = $SEARCHOPTION[$data['id_search_option']]['name']; } else { // Old record $is = $isr = $isa = $iso = false; switch ($data['itemtype_link']) { case 'Group': $is = 'isGroup'; break; case 'User': $is = 'isUser'; break; case 'Supplier': $is = 'isSupplier'; break; } if ($is) { $iditem = intval(substr($data['new_value'], strrpos($data['new_value'], '(') + 1)); // This is terrible idea $isr = $item->$is(CommonITILActor::REQUESTER, $iditem); $isa = $item->$is(CommonITILActor::ASSIGN, $iditem); $iso = $item->$is(CommonITILActor::OBSERVER, $iditem); } // Simple Heuristic, of course not enough if ($isr && !$isa && !$iso) { $as = _n('Requester', 'Requesters', 1); } else if (!$isr && $isa && !$iso) { $as = __('Assigned to'); } else if (!$isr && !$isa && $iso) { $as = _n('Watcher', 'Watchers', 1); } else { // Deleted or Ambiguous $as = false; } } if ($as) { $tmp['change'] = sprintf( __('%1$s: %2$s'), $action_label, sprintf(__('%1$s (%2$s)'), $data["new_value"], $as) ); } else { $tmp['change'] = sprintf(__('%1$s: %2$s'), $action_label, $data["new_value"]); } } break; case self::HISTORY_UPDATE_RELATION: $tmp['field'] = NOT_AVAILABLE; if ($linktype_field = explode('#', $data["itemtype_link"])) { $linktype = $linktype_field[0]; $tmp['field'] = is_a($linktype, CommonGLPI::class, true) ? $linktype::getTypeName() : $linktype; } $tmp['change'] = sprintf( __('%1$s: %2$s'), $action_label, sprintf(__('%1$s (%2$s)'), $data["old_value"], $data["new_value"]) ); break; case self::HISTORY_DEL_RELATION: $tmp['field'] = NOT_AVAILABLE; if ($item2 = getItemForItemtype($data["itemtype_link"])) { $tmp['field'] = $item2->getTypeName(1); } $tmp['change'] = sprintf(__('%1$s: %2$s'), $action_label, $data["old_value"]); break; case self::HISTORY_LOCK_RELATION: $tmp['field'] = NOT_AVAILABLE; if ($item2 = getItemForItemtype($data["itemtype_link"])) { $tmp['field'] = $item2->getTypeName(1); } $tmp['change'] = sprintf(__('%1$s: %2$s'), $action_label, $data["old_value"]); break; case self::HISTORY_UNLOCK_RELATION: $tmp['field'] = NOT_AVAILABLE; if ($item2 = getItemForItemtype($data["itemtype_link"])) { $tmp['field'] = $item2->getTypeName(1); } $tmp['change'] = sprintf(__('%1$s: %2$s'), $action_label, $data["new_value"]); break; case self::HISTORY_ADD_SUBITEM: $tmp['field'] = ''; if ($item2 = getItemForItemtype($data["itemtype_link"])) { $tmp['field'] = $item2->getTypeName(1); } $tmp['change'] = sprintf( __('%1$s: %2$s'), $action_label, sprintf(__('%1$s (%2$s)'), $tmp['field'], $data["new_value"]) ); break; case self::HISTORY_UPDATE_SUBITEM: $tmp['field'] = ''; if ($item2 = getItemForItemtype($data["itemtype_link"])) { $tmp['field'] = $item2->getTypeName(1); } $tmp['change'] = sprintf( __('%1$s: %2$s'), $action_label, sprintf(__('%1$s (%2$s)'), $tmp['field'], $data["new_value"]) ); break; case self::HISTORY_DELETE_SUBITEM: $tmp['field'] = ''; if ($item2 = getItemForItemtype($data["itemtype_link"])) { $tmp['field'] = $item2->getTypeName(1); } $tmp['change'] = sprintf( __('%1$s: %2$s'), $action_label, sprintf(__('%1$s (%2$s)'), $tmp['field'], $data["old_value"]) ); break; case self::HISTORY_LOCK_SUBITEM: $tmp['field'] = ''; if ($item2 = getItemForItemtype($data["itemtype_link"])) { $tmp['field'] = $item2->getTypeName(1); } $tmp['change'] = sprintf( __('%1$s: %2$s'), $action_label, sprintf(__('%1$s (%2$s)'), $tmp['field'], $data["old_value"]) ); break; case self::HISTORY_UNLOCK_SUBITEM: $tmp['field'] = ''; if ($item2 = getItemForItemtype($data["itemtype_link"])) { $tmp['field'] = $item2->getTypeName(1); } $tmp['change'] = sprintf( __('%1$s: %2$s'), $action_label, sprintf(__('%1$s (%2$s)'), $tmp['field'], $data["new_value"]) ); break; default: $fct = [$data['itemtype_link'], 'getHistoryEntry']; if ( ($data['linked_action'] >= self::HISTORY_PLUGIN) && $data['itemtype_link'] && is_callable($fct) ) { $tmp['field'] = $data['itemtype_link']::getTypeName(1); $tmp['change'] = call_user_func($fct, $data); } $tmp['display_history'] = !empty($tmp['change']); } } else { $fieldname = ""; $searchopt = []; $tablename = ''; // It's not an internal device foreach ($SEARCHOPTION as $key2 => $val2) { if ($key2 === $data["id_search_option"]) { $tmp['field'] = $val2["name"]; $tablename = $val2["table"]; $fieldname = $val2["field"]; $searchopt = $val2; if (isset($val2['datatype'])) { $tmp['datatype'] = $val2["datatype"]; } break; } } if ( ($itemtable == $tablename) || ($tmp['datatype'] == 'right') ) { switch ($tmp['datatype']) { // specific case for text field case 'text': $tmp['change'] = __('Update of the field'); break; default: $data["old_value"] = $item->getValueToDisplay($searchopt, $data["old_value"]); $data["new_value"] = $item->getValueToDisplay($searchopt, $data["new_value"]); break; } } if (empty($tmp['change'])) { $newval = $data["new_value"]; $oldval = $data["old_value"]; if ($data['id_search_option'] == '70') { $newval_expl = explode(' ', $newval); $oldval_expl = explode(' ', $oldval); if ($oldval_expl[0] == ' ') { $oldval = $data["old_value"]; } else { $old_iterator = $DBread->request('glpi_users', ['name' => $oldval_expl[0]]); foreach ($old_iterator as $val) { $oldval = sprintf( __('%1$s %2$s'), formatUserName( $val['id'], $oldval_expl[0], $val['realname'], $val['firstname'] ), ($oldval_expl[1] ?? "0") ); } } if ($newval_expl[0] == ' ') { $newval = $data["new_value"]; } else { $new_iterator = $DBread->request('glpi_users', ['name' => $newval_expl[0]]); foreach ($new_iterator as $val) { $newval = sprintf( __('%1$s %2$s'), formatUserName( $val['id'], $newval_expl[0], $val['realname'], $val['firstname'] ), ($newval_expl[1] ?? "0") ); } } } $tmp['change'] = sprintf(__('Change %1$s to %2$s'), "<del>$oldval</del>", "<ins>$newval</ins>"); } } $changes[] = $tmp; } return $changes; } /** * Retrieve distinct values for user_name field in item log. * Return is made to be used as select tag options. * * @param CommonDBTM $item Object instance * * @return array * * @since 9.3 **/ public static function getDistinctUserNamesValuesInItemLog(CommonDBTM $item) { /** @var \DBmysql $DB */ global $DB; $itemtype = $item->getType(); $items_id = $item->getField('id'); $iterator = $DB->request([ 'SELECT' => 'user_name', 'DISTINCT' => true, 'FROM' => self::getTable(), 'WHERE' => [ 'items_id' => $items_id, 'itemtype' => $itemtype ], 'ORDER' => 'id DESC' ]); $values = []; foreach ($iterator as $data) { if (empty($data['user_name'])) { continue; } $values[$data['user_name']] = $data['user_name']; } asort($values, SORT_NATURAL | SORT_FLAG_CASE); return $values; } /** * Retrieve distinct values for affected field in item log. * Return is made to be used as select tag options. * * @param CommonDBTM $item Object instance * * @return array * * @since 9.3 **/ public static function getDistinctAffectedFieldValuesInItemLog(CommonDBTM $item) { /** @var \DBmysql $DB */ global $DB; $itemtype = $item->getType(); $items_id = $item->getField('id'); $affected_fields = ['linked_action', 'itemtype_link', 'id_search_option']; $iterator = $DB->request([ 'SELECT' => $affected_fields, 'FROM' => self::getTable(), 'WHERE' => [ 'items_id' => $items_id, 'itemtype' => $itemtype ], 'GROUPBY' => $affected_fields, 'ORDER' => 'id DESC' ]); $values = []; foreach ($iterator as $data) { $key = null; $value = null; // This is an internal device ? if ($data["linked_action"]) { // Yes it is an internal device switch ($data["linked_action"]) { case self::HISTORY_ADD_DEVICE: case self::HISTORY_DELETE_DEVICE: case self::HISTORY_LOCK_DEVICE: case self::HISTORY_UNLOCK_DEVICE: case self::HISTORY_DISCONNECT_DEVICE: case self::HISTORY_CONNECT_DEVICE: case self::HISTORY_ADD_RELATION: case self::HISTORY_DEL_RELATION: case self::HISTORY_LOCK_RELATION: case self::HISTORY_UNLOCK_RELATION: case self::HISTORY_ADD_SUBITEM: case self::HISTORY_UPDATE_SUBITEM: case self::HISTORY_DELETE_SUBITEM: case self::HISTORY_UPDATE_RELATION: case self::HISTORY_LOCK_SUBITEM: case self::HISTORY_UNLOCK_SUBITEM: $linked_action_values = [ self::HISTORY_ADD_DEVICE, self::HISTORY_DELETE_DEVICE, self::HISTORY_LOCK_DEVICE, self::HISTORY_UNLOCK_DEVICE, self::HISTORY_DISCONNECT_DEVICE, self::HISTORY_CONNECT_DEVICE, self::HISTORY_ADD_RELATION, self::HISTORY_UPDATE_RELATION, self::HISTORY_DEL_RELATION, self::HISTORY_LOCK_RELATION, self::HISTORY_UNLOCK_RELATION, self::HISTORY_ADD_SUBITEM, self::HISTORY_UPDATE_SUBITEM, self::HISTORY_DELETE_SUBITEM, self::HISTORY_LOCK_SUBITEM, self::HISTORY_UNLOCK_SUBITEM, ]; $key = 'linked_action::' . implode(',', $linked_action_values) . ';' . 'itemtype_link::' . $data['itemtype_link'] . ';'; if ($linked_item = getItemForItemtype($data["itemtype_link"])) { if ($linked_item instanceof Item_Devices) { $value = $linked_item->getDeviceTypeName(1); } else { $value = $linked_item->getTypeName(1); } } break; case self::HISTORY_UPDATE_DEVICE: $key = 'linked_action::' . self::HISTORY_UPDATE_DEVICE . ';' . 'itemtype_link::' . $data['itemtype_link'] . ';'; $linktype_field = explode('#', $data["itemtype_link"]); $linktype = $linktype_field[0]; $field = $linktype_field[1]; $devicetype = $linktype::getDeviceType(); $specif_fields = $linktype::getSpecificities(); $value = $devicetype; if (isset($specif_fields[$field]['short name'])) { $value .= " (" . $specif_fields[$field]['short name'] . ")"; } break; case self::HISTORY_INSTALL_SOFTWARE: case self::HISTORY_UNINSTALL_SOFTWARE: $linked_action_values = [ self::HISTORY_INSTALL_SOFTWARE, self::HISTORY_UNINSTALL_SOFTWARE, ]; $key = 'linked_action::' . implode(',', $linked_action_values) . ';'; $value = _n('Software', 'Software', 1); break; default: $linked_action_values_to_exclude = [ 0, //Exclude lines corresponding to no action. self::HISTORY_ADD_DEVICE, self::HISTORY_DELETE_DEVICE, self::HISTORY_LOCK_DEVICE, self::HISTORY_UNLOCK_DEVICE, self::HISTORY_DISCONNECT_DEVICE, self::HISTORY_CONNECT_DEVICE, self::HISTORY_ADD_RELATION, self::HISTORY_UPDATE_RELATION, self::HISTORY_DEL_RELATION, self::HISTORY_LOCK_RELATION, self::HISTORY_UNLOCK_RELATION, self::HISTORY_ADD_SUBITEM, self::HISTORY_UPDATE_SUBITEM, self::HISTORY_DELETE_SUBITEM, self::HISTORY_LOCK_SUBITEM, self::HISTORY_UNLOCK_SUBITEM, self::HISTORY_UPDATE_DEVICE, self::HISTORY_INSTALL_SOFTWARE, self::HISTORY_UNINSTALL_SOFTWARE, ]; $key = 'linked_action:NOT:' . implode(',', $linked_action_values_to_exclude) . ';'; $value = __('Others'); break; } } else { // It's not an internal device foreach (Search::getOptions($itemtype) as $search_opt_key => $search_opt_val) { if ($search_opt_key == $data["id_search_option"]) { $key = 'id_search_option::' . $data['id_search_option'] . ';'; $value = $search_opt_val["name"]; break; } } } if (null !== $key && null !== $value) { $values[$key] = $value; } } uasort( $values, function ($a, $b) { $other = __('Others'); if ($a === $other) { return 1; } else if ($b === $other) { return -1; } return strcmp($a, $b); } ); return $values; } /** * Retrieve distinct values for action in item log. * Return is made to be used as select tag options. * * @param CommonDBTM $item Object instance * * @return array * * @since 9.3 **/ public static function getDistinctLinkedActionValuesInItemLog(CommonDBTM $item) { /** @var \DBmysql $DB */ global $DB; $itemtype = $item->getType(); $items_id = $item->getField('id'); $iterator = $DB->request([ 'SELECT' => 'linked_action', 'DISTINCT' => true, 'FROM' => self::getTable(), 'WHERE' => [ 'items_id' => $items_id, 'itemtype' => $itemtype ], 'ORDER' => 'id DESC' ]); $values = []; foreach ($iterator as $data) { $key = $data["linked_action"]; $value = null; // This is an internal device ? if ($data["linked_action"]) { $value = self::getLinkedActionLabel($data["linked_action"]); if (null === $value) { $key = 'other'; $value = __('Others'); } } else { $value = __('Update a field'); } if (null !== $value) { $values[$key] = $value; } } uasort( $values, function ($a, $b) { $other = __('Others'); if ($a === $other) { return 1; } else if ($b === $other) { return -1; } return strcmp($a, $b); } ); return $values; } /** * Returns label corresponding to the linked action of a log entry. * * @param integer $linked_action Linked action value of a log entry. * * @return string * * @since 9.3 **/ public static function getLinkedActionLabel($linked_action) { $label = null; switch ($linked_action) { case self::HISTORY_CREATE_ITEM: $label = __('Add the item'); break; case self::HISTORY_DELETE_ITEM: $label = __('Delete the item'); break; case self::HISTORY_LOCK_ITEM: $label = __('Lock the item'); break; case self::HISTORY_UNLOCK_ITEM: $label = __('Unlock the item'); break; case self::HISTORY_RESTORE_ITEM: $label = __('Restore the item'); break; case self::HISTORY_ADD_DEVICE: $label = __('Add a component'); break; case self::HISTORY_UPDATE_DEVICE: $label = __('Change a component'); break; case self::HISTORY_DELETE_DEVICE: $label = __('Delete a component'); break; case self::HISTORY_LOCK_DEVICE: $label = __('Lock a component'); break; case self::HISTORY_UNLOCK_DEVICE: $label = __('Unlock a component'); break; case self::HISTORY_INSTALL_SOFTWARE: $label = __('Install a software'); break; case self::HISTORY_UNINSTALL_SOFTWARE: $label = __('Uninstall a software'); break; case self::HISTORY_DISCONNECT_DEVICE: $label = __('Disconnect an item'); break; case self::HISTORY_CONNECT_DEVICE: $label = __('Connect an item'); break; case self::HISTORY_ADD_RELATION: $label = __('Add a link with an item'); break; case self::HISTORY_UPDATE_RELATION: $label = __('Update a link with an item'); break; case self::HISTORY_DEL_RELATION: $label = __('Delete a link with an item'); break; case self::HISTORY_LOCK_RELATION: $label = __('Lock a link with an item'); break; case self::HISTORY_UNLOCK_RELATION: $label = __('Unlock a link with an item'); break; case self::HISTORY_ADD_SUBITEM: $label = __('Add an item'); break; case self::HISTORY_UPDATE_SUBITEM: $label = __('Update an item'); break; case self::HISTORY_DELETE_SUBITEM: $label = __('Delete an item'); break; case self::HISTORY_LOCK_SUBITEM: $label = __('Lock an item'); break; case self::HISTORY_UNLOCK_SUBITEM: $label = __('Unlock an item'); break; case self::HISTORY_LOG_SIMPLE_MESSAGE: default: break; } return $label; } /** * Convert filters values into SQL filters usable in 'WHERE' condition of request build with 'DBmysqlIterator'. * * @param array $filters Filters values. * Filters values must be passed as indexed array using following rules : * - 'affected_fields' key for values corresponding to values built in 'self::getDistinctAffectedFieldValuesInItemLog()', * - 'date' key for a date value in 'Y-m-d H:i:s' format, * - 'linked_actions' key for values corresponding to values built in 'self::getDistinctLinkedActionValuesInItemLog()', * - 'users_names' key for values corresponding to values built in 'self::getDistinctUserNamesValuesInItemLog()'. * * @return array * * @since 9.3 **/ public static function convertFiltersValuesToSqlCriteria(array $filters) { /** @var \DBmysql $DB */ global $DB; $sql_filters = []; if (isset($filters['affected_fields']) && !empty($filters['affected_fields'])) { $affected_field_crit = []; foreach ($filters['affected_fields'] as $index => $affected_field) { $affected_field_crit[$index] = []; foreach (explode(";", $affected_field) as $var) { if (1 === preg_match('/^(?P<key>.+):(?P<operator>.*):(?P<values>.+)$/', $var, $matches)) { $key = $matches['key']; $operator = $matches['operator']; // Each field can have multiple values for a given filter $values = explode(',', $matches['values']); // linked_action and id_search_option are stored as integers if (in_array($key, ['linked_action', 'id_search_option'])) { $values = array_map('intval', $values); } if (!empty($operator)) { $affected_field_crit[$index][$operator][$key] = $values; } else { $affected_field_crit[$index][$key] = $values; } } } } $sql_filters[] = [ 'OR' => $affected_field_crit ]; } if (isset($filters['date']) && !empty($filters['date'])) { $sql_filters[] = [ ['date_mod' => ['>=', "{$filters['date']} 00:00:00"]], ['date_mod' => ['<=', "{$filters['date']} 23:59:59"]], ]; } if (isset($filters['linked_actions']) && !empty($filters['linked_actions'])) { $linked_action_crit = []; foreach ($filters['linked_actions'] as $linked_action) { if ($linked_action === 'other') { $linked_action_crit[] = ['linked_action' => self::HISTORY_LOG_SIMPLE_MESSAGE]; $linked_action_crit[] = ['linked_action' => ['>=', self::HISTORY_PLUGIN]]; } else { $linked_action_crit[] = ['linked_action' => $linked_action]; } } $sql_filters[] = ['OR' => $linked_action_crit]; } if (isset($filters['users_names']) && !empty($filters['users_names'])) { $sql_filters['user_name'] = $filters['users_names']; } return $sql_filters; } /** * Actions done after the ADD of the item in the database * * @since 0.83 * * @see CommonDBTM::post_addItem() **/ public function post_addItem() { $_SESSION['glpi_maxhistory'] = $this->fields['id']; } public function getRights($interface = 'central') { $values = [ READ => __('Read')]; return $values; } public static function useQueue(): void { static::$use_queue = true; } public static function queue($var): void { static::$queue[] = $var; } public static function resetQueue(): void { static::$queue = []; } public static function handleQueue(): void { /** @var \DBmysql $DB */ global $DB; $queue = static::$queue; if (!count($queue)) { return; } $update = $DB->buildInsert( static::getTable(), array_fill_keys(array_keys($queue[0]), new \QueryParam()) ); $stmt = $DB->prepare($update); foreach (static::$queue as $input) { $stmt->bind_param( str_pad('', count($input), 's'), ...array_values($input) ); $DB->executeStatement($stmt); } $stmt->close(); static::resetQueue(); } }