5576 lines
168 KiB
PHP
5576 lines
168 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/>.
|
|
* ---------------------------------------------------------------------
|
|
*/
|
|
|
|
use Glpi\Event;
|
|
|
|
if (!defined('GLPI_ROOT')) {
|
|
die("Sorry. You can't access this file directly");
|
|
}
|
|
|
|
/**
|
|
* Common DataBase Table Manager Class - Persistent Object
|
|
**/
|
|
class CommonDBTM extends CommonGLPI {
|
|
|
|
/**
|
|
* Data fields of the Item.
|
|
*
|
|
* @var mixed[]
|
|
*/
|
|
public $fields = [];
|
|
|
|
/**
|
|
* Flag to determine whether or not changes must be logged into history.
|
|
*
|
|
* @var boolean
|
|
*/
|
|
public $dohistory = false;
|
|
|
|
/**
|
|
* List of fields that must not be taken into account when logging history or computating last
|
|
* modification date.
|
|
*
|
|
* @var string[]
|
|
*/
|
|
public $history_blacklist = [];
|
|
|
|
/**
|
|
* Flag to determine whether or not automatic messages must be generated on actions.
|
|
*
|
|
* @var boolean
|
|
*/
|
|
public $auto_message_on_action = true;
|
|
|
|
/**
|
|
* Flag to determine whether or not a link to item form can be automatically generated via
|
|
* self::getLink() method.
|
|
*
|
|
* @var boolean
|
|
*/
|
|
public $no_form_page = false;
|
|
|
|
/**
|
|
* Flag to determine whether or not table name of item can be automatically generated via
|
|
* self::getTable() method.
|
|
*
|
|
* @var boolean
|
|
*/
|
|
static protected $notable = false;
|
|
|
|
/**
|
|
* List of fields that must not be taken into account for dictionnary processing.
|
|
*
|
|
* @var string[]
|
|
*/
|
|
public $additional_fields_for_dictionnary = [];
|
|
|
|
/**
|
|
* List of linked item types on which entities informations should be forwarded on update.
|
|
*
|
|
* @var string[]
|
|
*/
|
|
static protected $forward_entity_to = [];
|
|
|
|
/**
|
|
* Foreign key field cache : set dynamically calling getForeignKeyField
|
|
*
|
|
* @TODO Remove this variable as it is not used ?
|
|
*/
|
|
protected $fkfield = "";
|
|
|
|
/**
|
|
* Search option of item. Initialized on first call to self::getOptions() and used as cache.
|
|
*
|
|
* @var array
|
|
*
|
|
* @TODO Should be removed and replaced by real cache usage.
|
|
*/
|
|
protected $searchopt = false;
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
public $taborientation = 'vertical';
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
public $get_item_to_display_tab = true;
|
|
|
|
/**
|
|
* List of linked item types from plugins on which entities informations should be forwarded on update.
|
|
*
|
|
* @var array
|
|
*/
|
|
static protected $plugins_forward_entity = [];
|
|
|
|
/**
|
|
* Flag to determine whether or not table name of item has a notepad.
|
|
*
|
|
* @var boolean
|
|
*/
|
|
protected $usenotepad = false;
|
|
|
|
/**
|
|
* Flag to determine whether or not notification queu should be flushed immediately when an
|
|
* action is performed on item.
|
|
*
|
|
* @var boolean
|
|
*/
|
|
public $notificationqueueonaction = false;
|
|
|
|
/**
|
|
* Computed/forced values of classes tables.
|
|
* @var string[]
|
|
*/
|
|
protected static $tables_of = [];
|
|
|
|
/**
|
|
* Computed values of classes foreign keys.
|
|
* @var string[]
|
|
*/
|
|
protected static $foreign_key_fields_of = [];
|
|
|
|
|
|
/**
|
|
* Fields to remove when querying data with api
|
|
* @var array
|
|
*/
|
|
static $undisclosedFields = [];
|
|
|
|
/**
|
|
* Constructor
|
|
**/
|
|
function __construct () {
|
|
}
|
|
|
|
|
|
/**
|
|
* Return the table used to store this object
|
|
*
|
|
* @param string $classname Force class (to avoid late_binding on inheritance)
|
|
*
|
|
* @return string
|
|
**/
|
|
static function getTable($classname = null) {
|
|
if ($classname === null) {
|
|
$classname = get_called_class();
|
|
}
|
|
|
|
if (!class_exists($classname) || $classname::$notable) {
|
|
return '';
|
|
}
|
|
|
|
if (!isset(self::$tables_of[$classname]) || empty(self::$tables_of[$classname])) {
|
|
self::$tables_of[$classname] = getTableForItemType($classname);
|
|
}
|
|
|
|
return self::$tables_of[$classname];
|
|
}
|
|
|
|
|
|
/**
|
|
* force table value (used for config management for old versions)
|
|
*
|
|
* @param string $table name of the table to be forced
|
|
*
|
|
* @return void
|
|
**/
|
|
static function forceTable($table) {
|
|
self::$tables_of[get_called_class()] = $table;
|
|
}
|
|
|
|
|
|
static function getForeignKeyField() {
|
|
$classname = get_called_class();
|
|
|
|
if (!isset(self::$foreign_key_fields_of[$classname])
|
|
|| empty(self::$foreign_key_fields_of[$classname])) {
|
|
self::$foreign_key_fields_of[$classname] = getForeignKeyFieldForTable(static::getTable());
|
|
}
|
|
|
|
return self::$foreign_key_fields_of[$classname];
|
|
}
|
|
|
|
/**
|
|
* Return SQL path to access a field.
|
|
*
|
|
* @param string $field Name of the field (or SQL keyword like '*')
|
|
* @param string|null $classname Forced classname (to avoid late_binding on inheritance)
|
|
*
|
|
* @return string
|
|
*
|
|
* @throws InvalidArgumentException
|
|
* @throws LogicException
|
|
**/
|
|
static function getTableField($field, $classname = null) {
|
|
|
|
if (empty($field)) {
|
|
throw new InvalidArgumentException('Argument $field cannot be empty.');
|
|
}
|
|
|
|
$tablename = self::getTable($classname);
|
|
if (empty($tablename)) {
|
|
throw new LogicException('Invalid table name.');
|
|
}
|
|
|
|
return sprintf('%s.%s', $tablename, $field);
|
|
}
|
|
|
|
/**
|
|
* Retrieve an item from the database
|
|
*
|
|
* @param integer $ID ID of the item to get
|
|
*
|
|
* @return boolean true if succeed else false
|
|
**/
|
|
function getFromDB($ID) {
|
|
global $DB;
|
|
// Make new database object and fill variables
|
|
|
|
// != 0 because 0 is consider as empty
|
|
if (strlen($ID) == 0) {
|
|
return false;
|
|
}
|
|
|
|
$iterator = $DB->request([
|
|
'FROM' => $this->getTable(),
|
|
'WHERE' => [
|
|
$this->getTable() . '.' . $this->getIndexName() => Toolbox::cleanInteger($ID)
|
|
],
|
|
'LIMIT' => 1
|
|
]);
|
|
|
|
if (count($iterator) == 1) {
|
|
$this->fields = $iterator->next();
|
|
$this->post_getFromDB();
|
|
return true;
|
|
} else if (count($iterator) > 1) {
|
|
Toolbox::logWarning(
|
|
sprintf(
|
|
'getFromDB expects to get one result, %1$s found!',
|
|
count($iterator)
|
|
)
|
|
);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
/**
|
|
* Hydrate an object from a resultset row
|
|
*
|
|
* @param array $rs The row
|
|
*
|
|
* @return void
|
|
*/
|
|
function getFromResultSet($rs) {
|
|
//just set fields!
|
|
$this->fields = $rs;
|
|
}
|
|
|
|
|
|
/**
|
|
* Generator to browse object from an iterator
|
|
* @see http://php.net/manual/en/language.generators.syntax.php
|
|
*
|
|
* @since 9.2
|
|
*
|
|
* @param DBmysqlIterator $iter Iterator instance
|
|
*
|
|
* @return CommonDBTM
|
|
*/
|
|
public static function getFromIter(DBmysqlIterator $iter) {
|
|
$item = new static;
|
|
|
|
foreach ($iter as $row) {
|
|
if (!isset($row["id"])) {
|
|
continue;
|
|
}
|
|
if ($item->getFromDB($row["id"])) {
|
|
yield $item;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Get an object using some criteria
|
|
*
|
|
* @since 9.2
|
|
*
|
|
* @param Array $crit search criteria
|
|
*
|
|
* @return boolean|array
|
|
*/
|
|
public function getFromDBByCrit(array $crit) {
|
|
global $DB;
|
|
|
|
$crit = ['SELECT' => 'id',
|
|
'FROM' => $this->getTable(),
|
|
'WHERE' => $crit];
|
|
|
|
$iter = $DB->request($crit);
|
|
if (count($iter) == 1) {
|
|
$row = $iter->next();
|
|
return $this->getFromDB($row['id']);
|
|
} else if (count($iter) > 1) {
|
|
Toolbox::logWarning(
|
|
sprintf(
|
|
'getFromDBByCrit expects to get one result, %1$s found!',
|
|
count($iter)
|
|
)
|
|
);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
/**
|
|
* Retrieve an item from the database by request. The request is an array
|
|
* similar to the one expected in DB::request().
|
|
*
|
|
* @since 9.3
|
|
*
|
|
* @see DB::request()
|
|
*
|
|
* @param array $request expression
|
|
*
|
|
* @return boolean true if succeed else false
|
|
**/
|
|
public function getFromDBByRequest(array $request) {
|
|
global $DB;
|
|
|
|
// Limit the request to the useful expressions
|
|
$request = array_diff_key($request, [
|
|
'FROM' => '',
|
|
'SELECT' => '',
|
|
'COUNT' => '',
|
|
'GROUPBY' => '',
|
|
]);
|
|
$request['FROM'] = $this->getTable();
|
|
$request['SELECT'] = $this->getTable() . '.*';
|
|
|
|
$iterator = $DB->request($request);
|
|
if (count($iterator) == 1) {
|
|
$this->fields = $iterator->next();
|
|
$this->post_getFromDB();
|
|
return true;
|
|
} else if (count($iterator) > 1) {
|
|
Toolbox::logWarning(
|
|
sprintf(
|
|
'getFromDBByRequest expects to get one result, %1$s found!',
|
|
count($iterator)
|
|
)
|
|
);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Get the identifier of the current item
|
|
*
|
|
* @return integer ID
|
|
**/
|
|
function getID() {
|
|
|
|
if (isset($this->fields[static::getIndexName()])) {
|
|
return $this->fields[static::getIndexName()];
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
|
|
/**
|
|
* Actions done at the end of the getFromDB function
|
|
*
|
|
* @return void
|
|
**/
|
|
function post_getFromDB() {
|
|
}
|
|
|
|
|
|
/**
|
|
* Actions done to not show some fields when geting a single item from API calls
|
|
*
|
|
* @param array $fields Fields to unset undiscloseds
|
|
*
|
|
* @return void
|
|
*/
|
|
static public function unsetUndisclosedFields(&$fields) {
|
|
foreach (static::$undisclosedFields as $key) {
|
|
unset($fields[$key]);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Retrieve all items from the database
|
|
*
|
|
* @param array $condition condition used to search if needed (empty get all) (default '')
|
|
* @param array|string $order order field if needed (default '')
|
|
* @param integer $limit limit retrieved data if needed (default '')
|
|
*
|
|
* @return array all retrieved data in a associative array by id
|
|
**/
|
|
function find($condition = [], $order = [], $limit = null) {
|
|
global $DB;
|
|
|
|
$criteria = [
|
|
'FROM' => $this->getTable()
|
|
];
|
|
|
|
if (count($condition)) {
|
|
$criteria['WHERE'] = $condition;
|
|
}
|
|
|
|
if (!is_array($order)) {
|
|
$order = [$order];
|
|
}
|
|
if (count($order)) {
|
|
$criteria['ORDERBY'] = $order;
|
|
}
|
|
|
|
if ((int)$limit > 0) {
|
|
$criteria['LIMIT'] = (int)$limit;
|
|
}
|
|
|
|
$data = [];
|
|
$iterator = $DB->request($criteria);
|
|
while ($line = $iterator->next()) {
|
|
$data[$line['id']] = $line;
|
|
}
|
|
|
|
return $data;
|
|
}
|
|
|
|
|
|
/**
|
|
* Get the name of the index field
|
|
*
|
|
* @return string name of the index field
|
|
**/
|
|
static function getIndexName() {
|
|
return "id";
|
|
}
|
|
|
|
|
|
/**
|
|
* Get an empty item
|
|
*
|
|
*@return boolean true if succeed else false
|
|
**/
|
|
function getEmpty() {
|
|
global $DB;
|
|
|
|
//make an empty database object
|
|
$table = $this->getTable();
|
|
|
|
if (!empty($table) &&
|
|
($fields = $DB->listFields($table))) {
|
|
|
|
foreach (array_keys($fields) as $key) {
|
|
$this->fields[$key] = "";
|
|
}
|
|
} else {
|
|
return false;
|
|
}
|
|
|
|
if (array_key_exists('entities_id', $this->fields)
|
|
&& isset($_SESSION["glpiactive_entity"])) {
|
|
$this->fields['entities_id'] = $_SESSION["glpiactive_entity"];
|
|
}
|
|
|
|
$this->post_getEmpty();
|
|
|
|
// Call the plugin hook - $this->fields can be altered
|
|
Plugin::doHook("item_empty", $this);
|
|
return true;
|
|
}
|
|
|
|
|
|
/**
|
|
* Actions done at the end of the getEmpty function
|
|
*
|
|
* @return void
|
|
**/
|
|
function post_getEmpty() {
|
|
}
|
|
|
|
|
|
/**
|
|
* Get type to register log on
|
|
*
|
|
* @since 0.83
|
|
*
|
|
* @return array array of type + ID
|
|
**/
|
|
function getLogTypeID() {
|
|
return [$this->getType(), $this->fields['id']];
|
|
}
|
|
|
|
|
|
/**
|
|
* Update the item in the database
|
|
*
|
|
* @param string[] $updates fields to update
|
|
* @param string[] $oldvalues array of old values of the updated fields
|
|
*
|
|
* @return void
|
|
**/
|
|
function updateInDB($updates, $oldvalues = []) {
|
|
global $DB;
|
|
|
|
foreach ($updates as $field) {
|
|
if (isset($this->fields[$field])) {
|
|
$DB->update(
|
|
$this->getTable(),
|
|
[$field => $this->fields[$field]],
|
|
['id' => $this->fields['id']]
|
|
);
|
|
if ($DB->affectedRows() == 0) {
|
|
if (isset($oldvalues[$field])) {
|
|
unset($oldvalues[$field]);
|
|
}
|
|
}
|
|
} else {
|
|
// Clean oldvalues
|
|
if (isset($oldvalues[$field])) {
|
|
unset($oldvalues[$field]);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
if (count($oldvalues)) {
|
|
Log::constructHistory($this, $oldvalues, $this->fields);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
/**
|
|
* Add an item to the database
|
|
*
|
|
* @return integer|boolean new ID of the item is insert successfull else false
|
|
**/
|
|
function addToDB() {
|
|
global $DB;
|
|
|
|
$nb_fields = count($this->fields);
|
|
if ($nb_fields > 0) {
|
|
$params = [];
|
|
foreach ($this->fields as $key => $value) {
|
|
//FIXME: why is that handled here?
|
|
if (($this->getType() == 'ProfileRight') && ($value == '')) {
|
|
$value = 0;
|
|
}
|
|
$params[$key] = $value;
|
|
}
|
|
|
|
$result = $DB->insert($this->getTable(), $params);
|
|
if ($result) {
|
|
if (!isset($this->fields['id'])
|
|
|| is_null($this->fields['id'])
|
|
|| ($this->fields['id'] == 0)) {
|
|
$this->fields['id'] = $DB->insertId();
|
|
}
|
|
|
|
return $this->fields['id'];
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
/**
|
|
* Restore item = set deleted flag to 0
|
|
*
|
|
* @return boolean true if succeed else false
|
|
**/
|
|
function restoreInDB() {
|
|
global $DB;
|
|
|
|
if ($this->maybeDeleted()) {
|
|
$params = ['is_deleted' => 0];
|
|
// Auto set date_mod if exsist
|
|
if (isset($this->fields['date_mod'])) {
|
|
$params['date_mod'] = $_SESSION["glpi_currenttime"];
|
|
}
|
|
|
|
if ($DB->update($this->getTable(), $params, ['id' => $this->fields['id']])) {
|
|
return true;
|
|
}
|
|
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
/**
|
|
* Mark deleted or purge an item in the database
|
|
*
|
|
* @param boolean $force force the purge of the item (not used if the table do not have a deleted field)
|
|
* (default 0)
|
|
*
|
|
* @return boolean true if succeed else false
|
|
**/
|
|
function deleteFromDB($force = 0) {
|
|
global $DB;
|
|
|
|
if (($force == 1)
|
|
|| !$this->maybeDeleted()
|
|
|| ($this->useDeletedToLockIfDynamic()
|
|
&& !$this->isDynamic())) {
|
|
$this->cleanDBonPurge();
|
|
if ($this instanceof CommonDropdown) {
|
|
$this->cleanTranslations();
|
|
}
|
|
$this->cleanHistory();
|
|
$this->cleanRelationData();
|
|
$this->cleanRelationTable();
|
|
|
|
$result = $DB->delete(
|
|
$this->getTable(), [
|
|
'id' => $this->fields['id']
|
|
]
|
|
);
|
|
if ($result) {
|
|
$this->post_deleteFromDB();
|
|
return true;
|
|
}
|
|
|
|
} else {
|
|
// Auto set date_mod if exsist
|
|
$toadd = [];
|
|
if (isset($this->fields['date_mod'])) {
|
|
$toadd['date_mod'] = $_SESSION["glpi_currenttime"];
|
|
}
|
|
|
|
$result = $DB->update(
|
|
$this->getTable(), [
|
|
'is_deleted' => 1
|
|
] + $toadd, [
|
|
'id' => $this->fields['id']
|
|
]
|
|
);
|
|
$this->cleanDBonMarkDeleted();
|
|
|
|
if ($result) {
|
|
return true;
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
/**
|
|
* Clean data in the tables which have linked the deleted item
|
|
*
|
|
* @return void
|
|
**/
|
|
function cleanHistory() {
|
|
global $DB;
|
|
|
|
if ($this->dohistory) {
|
|
$DB->delete(
|
|
'glpi_logs', [
|
|
'itemtype' => $this->getType(),
|
|
'items_id' => $this->fields['id']
|
|
]
|
|
);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Clean data in the tables which have linked the deleted item
|
|
* Clear 1/N Relation
|
|
*
|
|
* @return void
|
|
**/
|
|
function cleanRelationData() {
|
|
global $DB, $CFG_GLPI;
|
|
|
|
$RELATION = getDbRelations();
|
|
if (isset($RELATION[$this->getTable()])) {
|
|
$newval = (isset($this->input['_replace_by']) ? $this->input['_replace_by'] : 0);
|
|
|
|
foreach ($RELATION[$this->getTable()] as $tablename => $field) {
|
|
if ($tablename[0] != '_') {
|
|
|
|
$itemtype = getItemTypeForTable($tablename);
|
|
|
|
// Code factorization : we transform the singleton to an array
|
|
if (!is_array($field)) {
|
|
$field = [$field];
|
|
}
|
|
|
|
foreach ($field as $f) {
|
|
$result = $DB->request(
|
|
[
|
|
'FROM' => $tablename,
|
|
'WHERE' => [$f => $this->getID()],
|
|
]
|
|
);
|
|
foreach ($result as $data) {
|
|
// Be carefull : we must use getIndexName because self::update rely on that !
|
|
if ($object = getItemForItemtype($itemtype)) {
|
|
$idName = $object->getIndexName();
|
|
// And we must ensure that the index name is not the same as the field
|
|
// we try to modify. Otherwise we will loose this element because all
|
|
// will be set to $newval ...
|
|
if ($idName != $f) {
|
|
$object->update([$idName => $data[$idName],
|
|
$f => $newval,
|
|
'_disablenotif' => true]); // Disable notifs
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
// Clean ticket open against the item
|
|
if (in_array($this->getType(), $CFG_GLPI["ticket_types"])) {
|
|
$job = new Ticket();
|
|
$itemsticket = new Item_Ticket();
|
|
|
|
$iterator = $DB->request([
|
|
'FROM' => 'glpi_items_tickets',
|
|
'WHERE' => [
|
|
'items_id' => $this->getID(),
|
|
'itemtype' => $this->getType()
|
|
]
|
|
]);
|
|
|
|
while ($data = $iterator->next()) {
|
|
$cnt = countElementsInTable('glpi_items_tickets', ['tickets_id' => $data['tickets_id']]);
|
|
$itemsticket->delete(["id" => $data["id"]]);
|
|
if ($cnt == 1 && !$CFG_GLPI["keep_tickets_on_delete"]) {
|
|
$job->delete(["id" => $data["tickets_id"]]);
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Actions done after the DELETE of the item in the database
|
|
*
|
|
* @return void
|
|
**/
|
|
function post_deleteFromDB() {
|
|
}
|
|
|
|
|
|
/**
|
|
* Actions done when item is deleted from the database
|
|
*
|
|
* @return void
|
|
**/
|
|
function cleanDBonPurge() {
|
|
}
|
|
|
|
|
|
/**
|
|
* Delete children items and relation with other items from database.
|
|
*
|
|
* @param array $relations_classes List of classname on which deletion will be done
|
|
* Classes needs to extends CommonDBConnexity.
|
|
*
|
|
* @return void
|
|
**/
|
|
protected function deleteChildrenAndRelationsFromDb(array $relations_classes) {
|
|
|
|
foreach ($relations_classes as $classname) {
|
|
if (!is_a($classname, CommonDBConnexity::class, true)) {
|
|
Toolbox::logWarning(
|
|
sprintf(
|
|
'Unable to clean elements of class %s as it does not extends "CommonDBConnexity"',
|
|
$classname
|
|
)
|
|
);
|
|
continue;
|
|
}
|
|
|
|
/** @var CommonDBConnexity $relation_item */
|
|
$relation_item = new $classname();
|
|
$relation_item->cleanDBonItemDelete($this->getType(), $this->fields['id']);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Clean translations associated to a dropdown
|
|
*
|
|
* @since 0.85
|
|
*
|
|
* @return void
|
|
**/
|
|
function cleanTranslations() {
|
|
|
|
//Do not try to clean is dropdown translation is globally off
|
|
if (DropdownTranslation::isDropdownTranslationActive()) {
|
|
$translation = new DropdownTranslation();
|
|
$translation->deleteByCriteria(['itemtype' => get_class($this),
|
|
'items_id' => $this->getID()]);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Clean the data in the relation tables for the deleted item
|
|
* Clear N/N Relation
|
|
*
|
|
* @return void
|
|
**/
|
|
function cleanRelationTable() {
|
|
global $CFG_GLPI, $DB;
|
|
|
|
// If this type have INFOCOM, clean one associated to purged item
|
|
if (Infocom::canApplyOn($this)) {
|
|
$infocom = new Infocom();
|
|
|
|
if ($infocom->getFromDBforDevice($this->getType(), $this->fields['id'])) {
|
|
$infocom->delete(['id' => $infocom->fields['id']]);
|
|
}
|
|
}
|
|
|
|
// If this type have NETPORT, clean one associated to purged item
|
|
if (in_array($this->getType(), $CFG_GLPI['networkport_types'])) {
|
|
// If we don't use delete, then cleanDBonPurge() is not call and the NetworkPorts are not
|
|
// clean properly
|
|
$networkPortObject = new NetworkPort();
|
|
$networkPortObject->cleanDBonItemDelete($this->getType(), $this->getID());
|
|
// Manage networkportmigration if exists
|
|
if ($DB->tableExists('glpi_networkportmigrations')) {
|
|
$networkPortMigObject = new NetworkPortMigration();
|
|
$networkPortMigObject->cleanDBonItemDelete($this->getType(), $this->getID());
|
|
}
|
|
}
|
|
|
|
// If this type is RESERVABLE clean one associated to purged item
|
|
if (in_array($this->getType(), $CFG_GLPI['reservation_types'])) {
|
|
$rr = new ReservationItem();
|
|
$rr->cleanDBonItemDelete($this->getType(), $this->fields['id']);
|
|
}
|
|
|
|
// If this type have CONTRACT, clean one associated to purged item
|
|
if (in_array($this->getType(), $CFG_GLPI['contract_types'])) {
|
|
$ci = new Contract_Item();
|
|
$ci->cleanDBonItemDelete($this->getType(), $this->fields['id']);
|
|
}
|
|
|
|
// If this type have DOCUMENT, clean one associated to purged item
|
|
if (Document::canApplyOn($this)) {
|
|
$di = new Document_Item();
|
|
$di->cleanDBonItemDelete($this->getType(), $this->fields['id']);
|
|
}
|
|
|
|
// If this type have NOTEPAD, clean one associated to purged item
|
|
if ($this->usenotepad) {
|
|
$note = new Notepad();
|
|
$note->cleanDBonItemDelete($this->getType(), $this->fields['id']);
|
|
}
|
|
|
|
// Delete relations with KB
|
|
if (in_array($this->getType(), $CFG_GLPI['kb_types'])) {
|
|
$kbitem_item = new KnowbaseItem_Item();
|
|
$kbitem_item->cleanDBonItemDelete($this->getType(), $this->fields['id']);
|
|
}
|
|
|
|
if (in_array($this->getType(), $CFG_GLPI['ticket_types'])) {
|
|
//delete relation beetween item and changes/problems
|
|
$this->deleteChildrenAndRelationsFromDb(
|
|
[
|
|
Change_Item::class,
|
|
Item_Problem::class,
|
|
]
|
|
);
|
|
}
|
|
|
|
if (in_array($this->getType(), $CFG_GLPI['rackable_types'])) {
|
|
//delete relation beetween rackable type and its rack
|
|
$item_rack = new Item_Rack();
|
|
$item_rack->deleteByCriteria(
|
|
[
|
|
'itemtype' => $this->getType(),
|
|
'items_id' => $this->fields['id']
|
|
]
|
|
);
|
|
|
|
$item_enclosure = new Item_Enclosure();
|
|
$item_enclosure->deleteByCriteria(
|
|
[
|
|
'itemtype' => $this->getType(),
|
|
'items_id' => $this->fields['id']
|
|
]
|
|
);
|
|
}
|
|
|
|
if (in_array($this->getType(), $CFG_GLPI['cluster_types'])) {
|
|
//delete relation beetween clusterable elements type and their cluster
|
|
$this->deleteChildrenAndRelationsFromDb(
|
|
[
|
|
Item_Cluster::class,
|
|
]
|
|
);
|
|
}
|
|
|
|
if (in_array($this->getType(), $CFG_GLPI['operatingsystem_types'])) {
|
|
$this->deleteChildrenAndRelationsFromDb([
|
|
Item_OperatingSystem::class
|
|
]);
|
|
}
|
|
|
|
if (in_array($this->getType(), $CFG_GLPI['software_types'])) {
|
|
$this->deleteChildrenAndRelationsFromDb([
|
|
Item_SoftwareVersion::class
|
|
]);
|
|
}
|
|
|
|
if (in_array($this->getType(), $CFG_GLPI['kanban_types'])) {
|
|
$this->deleteChildrenAndRelationsFromDb([
|
|
Item_Kanban::class
|
|
]);
|
|
}
|
|
|
|
if (in_array($this->getType(), $CFG_GLPI['domain_types'])) {
|
|
$this->deleteChildrenAndRelationsFromDb([
|
|
Domain_Item::class
|
|
]);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Actions done when item flag deleted is set to an item
|
|
*
|
|
* @return void
|
|
**/
|
|
function cleanDBonMarkDeleted() {
|
|
}
|
|
|
|
|
|
/**
|
|
* Save the input data in the Session
|
|
*
|
|
* @since 0.84
|
|
*
|
|
* @return void
|
|
**/
|
|
protected function saveInput() {
|
|
$_SESSION['saveInput'][$this->getType()] = $this->input;
|
|
}
|
|
|
|
|
|
/**
|
|
* Clear the saved data stored in the session
|
|
*
|
|
* @since 0.84
|
|
*
|
|
* @return void
|
|
**/
|
|
protected function clearSavedInput() {
|
|
unset($_SESSION['saveInput'][$this->getType()]);
|
|
}
|
|
|
|
|
|
/**
|
|
* Get the data saved in the session
|
|
*
|
|
* @since 0.84
|
|
*
|
|
* @param array $default Array of value used if session is empty
|
|
*
|
|
* @return array Array of value
|
|
**/
|
|
protected function restoreInput(Array $default = []) {
|
|
|
|
if (isset($_SESSION['saveInput'][$this->getType()])) {
|
|
$saved = Html::cleanPostForTextArea($_SESSION['saveInput'][$this->getType()]);
|
|
|
|
// clear saved data when restored (only need once)
|
|
$this->clearSavedInput();
|
|
|
|
return $saved;
|
|
}
|
|
|
|
return $default;
|
|
}
|
|
|
|
|
|
// Common functions
|
|
/**
|
|
* Add an item in the database with all it's items.
|
|
*
|
|
* @param array $input the _POST vars returned by the item form when press add
|
|
* @param array $options with the insert options
|
|
* - unicity_message : do not display message if item it a duplicate (default is yes)
|
|
* @param boolean $history do history log ? (true by default)
|
|
*
|
|
* @return integer the new ID of the added item (or false if fail)
|
|
**/
|
|
function add(array $input, $options = [], $history = true) {
|
|
global $DB, $CFG_GLPI;
|
|
|
|
if ($DB->isSlave()) {
|
|
return false;
|
|
}
|
|
|
|
// This means we are not adding a cloned object
|
|
if (!isset($input['clone'])) {
|
|
// This means we are asked to clone the object (old way). This will clone the clone method
|
|
// that will set the clone parameter to true
|
|
if (isset($input['_oldID'])) {
|
|
$id_to_clone = $input['_oldID'];
|
|
}
|
|
if (isset($input['id'])) {
|
|
$id_to_clone = $input['id'];
|
|
}
|
|
if (isset($id_to_clone) && $this->getFromDB($id_to_clone)) {
|
|
if ($clone_id = $this->clone($input, $history)) {
|
|
$this->getFromDB($clone_id); // Load created items fields
|
|
}
|
|
return $clone_id;
|
|
}
|
|
}
|
|
|
|
// Store input in the object to be available in all sub-method / hook
|
|
$this->input = $input;
|
|
|
|
// Manage the _no_history
|
|
if (!isset($this->input['_no_history'])) {
|
|
$this->input['_no_history'] = !$history;
|
|
}
|
|
|
|
if (isset($this->input['add'])) {
|
|
// Input from the interface
|
|
// Save this data to be available if add fail
|
|
$this->saveInput();
|
|
}
|
|
|
|
// Call the plugin hook - $this->input can be altered
|
|
// This hook get the data from the form, not yet altered
|
|
Plugin::doHook("pre_item_add", $this);
|
|
|
|
if ($this->input && is_array($this->input)) {
|
|
|
|
if (isset($this->input['add'])) {
|
|
$this->input['_add'] = $this->input['add'];
|
|
unset($this->input['add']);
|
|
}
|
|
|
|
$this->input = $this->prepareInputForAdd($this->input);
|
|
}
|
|
|
|
if ($this->input && is_array($this->input)) {
|
|
// Call the plugin hook - $this->input can be altered
|
|
// This hook get the data altered by the object method
|
|
Plugin::doHook("post_prepareadd", $this);
|
|
}
|
|
|
|
if ($this->input && is_array($this->input)) {
|
|
//Check values to inject
|
|
$this->filterValues(!isCommandLine());
|
|
}
|
|
|
|
//Process business rules for assets
|
|
$this->assetBusinessRules(\RuleAsset::ONADD);
|
|
|
|
if ($this->input && is_array($this->input)) {
|
|
$this->fields = [];
|
|
$table_fields = $DB->listFields($this->getTable());
|
|
|
|
// fill array for add
|
|
foreach (array_keys($this->input) as $key) {
|
|
if (($key[0] != '_')
|
|
&& isset($table_fields[$key])) {
|
|
$this->fields[$key] = $this->input[$key];
|
|
}
|
|
}
|
|
|
|
// Auto set date_creation if exsist
|
|
if (isset($table_fields['date_creation']) && !isset($this->input['date_creation'])) {
|
|
$this->fields['date_creation'] = $_SESSION["glpi_currenttime"];
|
|
}
|
|
|
|
// Auto set date_mod if exsist
|
|
if (isset($table_fields['date_mod']) && !isset($this->input['date_mod'])) {
|
|
$this->fields['date_mod'] = $_SESSION["glpi_currenttime"];
|
|
}
|
|
|
|
if ($this->checkUnicity(true, $options)) {
|
|
if ($this->addToDB() !== false) {
|
|
$this->post_addItem();
|
|
$this->addMessageOnAddAction();
|
|
|
|
if ($this->dohistory && $history) {
|
|
$changes = [
|
|
0,
|
|
'',
|
|
'',
|
|
];
|
|
Log::history($this->fields["id"], $this->getType(), $changes, 0,
|
|
Log::HISTORY_CREATE_ITEM);
|
|
}
|
|
|
|
// Auto create infocoms
|
|
if (isset($CFG_GLPI["auto_create_infocoms"]) && $CFG_GLPI["auto_create_infocoms"]
|
|
&& Infocom::canApplyOn($this)) {
|
|
|
|
$ic = new Infocom();
|
|
if (!$ic->getFromDBforDevice($this->getType(), $this->fields['id'])) {
|
|
$ic->add(['itemtype' => $this->getType(),
|
|
'items_id' => $this->fields['id']]);
|
|
}
|
|
}
|
|
|
|
// If itemtype is in infocomtype and if states_id field is filled
|
|
// and item is not a template
|
|
if (Infocom::canApplyOn($this)
|
|
&& isset($this->input['states_id'])
|
|
&& (!isset($this->input['is_template'])
|
|
|| !$this->input['is_template'])) {
|
|
|
|
//Check if we have to automatical fill dates
|
|
Infocom::manageDateOnStatusChange($this);
|
|
}
|
|
Plugin::doHook("item_add", $this);
|
|
|
|
// As add have suceed, clean the old input value
|
|
if (isset($this->input['_add'])) {
|
|
$this->clearSavedInput();
|
|
}
|
|
if ($this->notificationqueueonaction) {
|
|
QueuedNotification::forceSendFor($this->getType(), $this->fields['id']);
|
|
}
|
|
return $this->fields['id'];
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Clones the current item
|
|
*
|
|
* @since 9.5
|
|
*
|
|
* @param array $override_input custom input to override
|
|
* @param boolean $history do history log ? (true by default)
|
|
*
|
|
* @return integer the new ID of the clone (or false if fail)
|
|
*/
|
|
function clone(array $override_input = [], bool $history = true) {
|
|
global $DB, $CFG_GLPI;
|
|
|
|
if ($DB->isSlave()) {
|
|
return false;
|
|
}
|
|
$new_item = new static();
|
|
$input = Toolbox::addslashes_deep($this->fields);
|
|
foreach ($override_input as $key => $value) {
|
|
$input[$key] = $value;
|
|
}
|
|
$input = $new_item->prepareInputForClone($input);
|
|
if (isset($input['id'])) {
|
|
$input['_oldID'] = $input['id'];
|
|
unset($input['id']);
|
|
}
|
|
unset($input['date_creation']);
|
|
unset($input['date_mod']);
|
|
|
|
if (isset($input['template_name'])) {
|
|
unset($input['template_name']);
|
|
}
|
|
if (isset($input['is_template'])) {
|
|
unset($input['is_template']);
|
|
}
|
|
|
|
$input['clone'] = true;
|
|
$newID = $new_item->add($input, [], $history);
|
|
// If the item needs post clone (recursive cloning for example)
|
|
$new_item->post_clone($this, $history);
|
|
return $newID;
|
|
}
|
|
|
|
|
|
/**
|
|
* Get the link to an item
|
|
*
|
|
* @param array $options array of options
|
|
* - comments : boolean / display comments
|
|
* - complete : boolean / display completename instead of name
|
|
* - additional : boolean / display additionals information
|
|
* - linkoption : string / additional options to add to <a>
|
|
*
|
|
* @return string HTML link
|
|
**/
|
|
function getLink($options = []) {
|
|
|
|
$p = [
|
|
'linkoption' => '',
|
|
];
|
|
|
|
if (isset($options['linkoption'])) {
|
|
$p['linkoption'] = $options['linkoption'];
|
|
}
|
|
|
|
if (!isset($this->fields['id'])) {
|
|
return '';
|
|
}
|
|
|
|
if ($this->no_form_page
|
|
|| !$this->can($this->fields['id'], READ)) {
|
|
return $this->getNameID($options);
|
|
}
|
|
|
|
$link = $this->getLinkURL();
|
|
|
|
$label = $this->getNameID($options);
|
|
$title = '';
|
|
if (!preg_match('/title=/', $p['linkoption'])) {
|
|
$thename = $this->getName(['complete' => true]);
|
|
if ($thename != NOT_AVAILABLE) {
|
|
$title = ' title="' . htmlentities($thename, ENT_QUOTES, 'utf-8') . '"';
|
|
}
|
|
}
|
|
|
|
return "<a ".$p['linkoption']." href='$link' $title>$label</a>";
|
|
}
|
|
|
|
|
|
/**
|
|
* Get the link url to an item
|
|
*
|
|
* @return string HTML link
|
|
**/
|
|
function getLinkURL() {
|
|
|
|
if (!isset($this->fields['id'])) {
|
|
return '';
|
|
}
|
|
|
|
$link = $this->getFormURLWithID($this->getID());
|
|
$link .= ($this->isTemplate() ? "&withtemplate=1" : "");
|
|
|
|
return $link;
|
|
}
|
|
|
|
|
|
/**
|
|
* Add a message on add action
|
|
*
|
|
* @return void
|
|
**/
|
|
function addMessageOnAddAction() {
|
|
|
|
$addMessAfterRedirect = false;
|
|
if (isset($this->input['_add'])) {
|
|
$addMessAfterRedirect = true;
|
|
}
|
|
|
|
if (isset($this->input['_no_message'])
|
|
|| !$this->auto_message_on_action) {
|
|
$addMessAfterRedirect = false;
|
|
}
|
|
|
|
if ($addMessAfterRedirect) {
|
|
$link = $this->getFormURL();
|
|
if (!isset($link)) {
|
|
return;
|
|
}
|
|
if ($this->getName() == NOT_AVAILABLE) {
|
|
//TRANS: %1$s is the itemtype, %2$d is the id of the item
|
|
$this->fields['name'] = sprintf(__('%1$s - ID %2$d'),
|
|
$this->getTypeName(1), $this->fields['id']);
|
|
}
|
|
$display = (isset($this->input['_no_message_link'])?$this->getNameID()
|
|
:$this->getLink());
|
|
|
|
// Do not display quotes
|
|
//TRANS : %s is the description of the added item
|
|
Session::addMessageAfterRedirect(sprintf(__('%1$s: %2$s'), __('Item successfully added'),
|
|
stripslashes($display)));
|
|
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Add needed information to $input (example entities_id)
|
|
*
|
|
* @param array $input datas used to add the item
|
|
*
|
|
* @since 0.84
|
|
*
|
|
* @return array the modified $input array
|
|
**/
|
|
function addNeededInfoToInput($input) {
|
|
return $input;
|
|
}
|
|
|
|
|
|
/**
|
|
* Prepare input datas for adding the item
|
|
*
|
|
* @param array $input datas used to add the item
|
|
*
|
|
* @return array the modified $input array
|
|
**/
|
|
function prepareInputForAdd($input) {
|
|
return $input;
|
|
}
|
|
|
|
/**
|
|
* Prepare input datas for cloning the item
|
|
*
|
|
* @since 9.5
|
|
*
|
|
* @param array $input datas used to add the item
|
|
*
|
|
* @return array the modified $input array
|
|
**/
|
|
function prepareInputForClone($input) {
|
|
unset($input['id']);
|
|
unset($input['date_mod']);
|
|
unset($input['date_creation']);
|
|
return $input;
|
|
}
|
|
|
|
|
|
/**
|
|
* Actions done after the ADD of the item in the database
|
|
*
|
|
* @return void
|
|
**/
|
|
function post_addItem() {
|
|
}
|
|
|
|
/**
|
|
* Actions done after the clone of the item in the database
|
|
*
|
|
* @since 9.5
|
|
*
|
|
* @param $source the item that is being cloned
|
|
* @param $history do history log ?
|
|
*
|
|
* @return void
|
|
**/
|
|
function post_clone($source, $history) {
|
|
}
|
|
|
|
|
|
/**
|
|
* Update some elements of an item in the database.
|
|
*
|
|
* @param array $input the _POST vars returned by the item form when press update
|
|
* @param boolean $history do history log ? (default 1)
|
|
* @param array $options with the insert options
|
|
*
|
|
* @return boolean true on success
|
|
**/
|
|
function update(array $input, $history = 1, $options = []) {
|
|
global $DB, $GLPI_CACHE;
|
|
|
|
if ($DB->isSlave()) {
|
|
return false;
|
|
}
|
|
|
|
if (!$this->getFromDB($input[static::getIndexName()])) {
|
|
return false;
|
|
}
|
|
|
|
// Store input in the object to be available in all sub-method / hook
|
|
$this->input = $input;
|
|
|
|
// Manage the _no_history
|
|
if (!isset($this->input['_no_history'])) {
|
|
$this->input['_no_history'] = !$history;
|
|
}
|
|
|
|
// Plugin hook - $this->input can be altered
|
|
Plugin::doHook("pre_item_update", $this);
|
|
if ($this->input && is_array($this->input)) {
|
|
$this->input = $this->prepareInputForUpdate($this->input);
|
|
|
|
if (isset($this->input['update'])) {
|
|
$this->input['_update'] = $this->input['update'];
|
|
unset($this->input['update']);
|
|
}
|
|
$this->filterValues(!isCommandLine());
|
|
}
|
|
|
|
//Process business rules for assets
|
|
$this->assetBusinessRules(\RuleAsset::ONUPDATE);
|
|
|
|
// Valid input for update
|
|
if ($this->checkUnicity(false, $options)) {
|
|
if ($this->input && is_array($this->input)) {
|
|
// Fill the update-array with changes
|
|
$x = 0;
|
|
$this->updates = [];
|
|
$this->oldvalues = [];
|
|
|
|
foreach (array_keys($this->input) as $key) {
|
|
if (array_key_exists($key, $this->fields)) {
|
|
|
|
// Prevent history for date statement (for date for example)
|
|
if (is_null($this->fields[$key])
|
|
&& ($this->input[$key] == 'NULL')) {
|
|
$this->fields[$key] = 'NULL';
|
|
}
|
|
// Compare item
|
|
$ischanged = true;
|
|
$searchopt = $this->getSearchOptionByField('field', $key, $this->getTable());
|
|
if (isset($searchopt['datatype'])) {
|
|
switch ($searchopt['datatype']) {
|
|
case 'string' :
|
|
case 'text' :
|
|
$ischanged = (strcmp($DB->escape($this->fields[$key]),
|
|
$this->input[$key]) != 0);
|
|
break;
|
|
|
|
case 'itemlink' :
|
|
if ($key == 'name') {
|
|
$ischanged = (strcmp($DB->escape($this->fields[$key]),
|
|
$this->input[$key]) != 0);
|
|
break;
|
|
} // else default
|
|
|
|
default :
|
|
$ischanged = ($DB->escape($this->fields[$key]) != $this->input[$key]);
|
|
break;
|
|
}
|
|
} else {
|
|
// No searchoption case
|
|
$ischanged = ($DB->escape($this->fields[$key]) != $this->input[$key]);
|
|
|
|
}
|
|
if ($ischanged) {
|
|
if ($key != "id") {
|
|
|
|
// Store old values
|
|
if (!in_array($key, $this->history_blacklist)) {
|
|
$this->oldvalues[$key] = $this->fields[$key];
|
|
}
|
|
|
|
$this->fields[$key] = $this->input[$key];
|
|
$this->updates[$x] = $key;
|
|
$x++;
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
if (count($this->updates)) {
|
|
if (array_key_exists('date_mod', $this->fields)) {
|
|
// is a non blacklist field exists
|
|
if (count(array_diff($this->updates, $this->history_blacklist)) > 0) {
|
|
$this->fields['date_mod'] = $_SESSION["glpi_currenttime"];
|
|
$this->updates[$x++] = 'date_mod';
|
|
}
|
|
}
|
|
$this->pre_updateInDB();
|
|
|
|
if (count($this->updates)) {
|
|
if ($this->updateInDB($this->updates,
|
|
($this->dohistory && $history ? $this->oldvalues
|
|
: []))) {
|
|
$this->addMessageOnUpdateAction();
|
|
Plugin::doHook("item_update", $this);
|
|
|
|
//Fill forward_entity_to array with itemtypes coming from plugins
|
|
if (isset(self::$plugins_forward_entity[$this->getType()])) {
|
|
foreach (self::$plugins_forward_entity[$this->getType()] as $itemtype) {
|
|
static::$forward_entity_to[] = $itemtype;
|
|
}
|
|
}
|
|
// forward entity information if needed
|
|
if (count(static::$forward_entity_to)
|
|
&& (in_array("entities_id", $this->updates)
|
|
|| in_array("is_recursive", $this->updates))) {
|
|
$this->forwardEntityInformations();
|
|
}
|
|
|
|
// If itemtype is in infocomtype and if states_id field is filled
|
|
// and item not a template
|
|
if (Infocom::canApplyOn($this)
|
|
&& in_array('states_id', $this->updates)
|
|
&& ($this->getField('is_template') != NOT_AVAILABLE)) {
|
|
//Check if we have to automatical fill dates
|
|
Infocom::manageDateOnStatusChange($this, false);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
$this->post_updateItem($history);
|
|
|
|
if ($this->notificationqueueonaction) {
|
|
QueuedNotification::forceSendFor($this->getType(), $this->fields['id']);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
/**
|
|
* Forward entity information to linked items
|
|
*
|
|
* @return void
|
|
**/
|
|
protected function forwardEntityInformations() {
|
|
global $DB;
|
|
|
|
if (!isset($this->fields['id']) || !($this->fields['id'] >= 0)) {
|
|
return false;
|
|
}
|
|
|
|
if (count(static::$forward_entity_to)) {
|
|
foreach (static::$forward_entity_to as $type) {
|
|
$item = new $type();
|
|
$query = [
|
|
'SELECT' => ['id'],
|
|
'FROM' => $item->getTable()
|
|
];
|
|
|
|
$OR = [];
|
|
if ($item->isField('itemtype')) {
|
|
$OR[] = [
|
|
'itemtype' => $this->getType(),
|
|
'items_id' => $this->getID()
|
|
];
|
|
}
|
|
if ($item->isField($this->getForeignKeyField())) {
|
|
$OR[] = [$this->getForeignKeyField() => $this->getID()];
|
|
}
|
|
$query['WHERE'][] = ['OR' => $OR];
|
|
|
|
$input = [
|
|
'entities_id' => $this->getEntityID(),
|
|
'_transfer' => 1
|
|
];
|
|
if ($this->maybeRecursive()) {
|
|
$input['is_recursive'] = $this->isRecursive();
|
|
}
|
|
|
|
$iterator = $DB->request($query);
|
|
while ($data = $iterator->next()) {
|
|
$input['id'] = $data['id'];
|
|
// No history for such update
|
|
$item->update($input, 0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Add a message on update action
|
|
*
|
|
* @return void
|
|
**/
|
|
function addMessageOnUpdateAction() {
|
|
|
|
$addMessAfterRedirect = false;
|
|
|
|
if (isset($this->input['_update'])) {
|
|
$addMessAfterRedirect = true;
|
|
}
|
|
|
|
if (isset($this->input['_no_message'])
|
|
|| !$this->auto_message_on_action) {
|
|
$addMessAfterRedirect = false;
|
|
}
|
|
|
|
if ($addMessAfterRedirect) {
|
|
$link = $this->getFormURL();
|
|
if (!isset($link)) {
|
|
return;
|
|
}
|
|
// Do not display quotes
|
|
if (isset($this->fields['name'])) {
|
|
$this->fields['name'] = stripslashes($this->fields['name']);
|
|
} else {
|
|
//TRANS: %1$s is the itemtype, %2$d is the id of the item
|
|
$this->fields['name'] = sprintf(__('%1$s - ID %2$d'),
|
|
$this->getTypeName(1), $this->fields['id']);
|
|
}
|
|
|
|
if (isset($this->input['_no_message_link'])) {
|
|
$display = $this->getNameID();
|
|
} else {
|
|
$display = $this->getLink();
|
|
}
|
|
//TRANS : %s is the description of the updated item
|
|
Session::addMessageAfterRedirect(sprintf(__('%1$s: %2$s'), __('Item successfully updated'), $display));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
/**
|
|
* Prepare input datas for updating the item
|
|
*
|
|
* @param array $input data used to update the item
|
|
*
|
|
* @return array the modified $input array
|
|
**/
|
|
function prepareInputForUpdate($input) {
|
|
return $input;
|
|
}
|
|
|
|
|
|
/**
|
|
* Actions done after the UPDATE of the item in the database
|
|
*
|
|
* @param boolean $history store changes history ? (default 1)
|
|
*
|
|
* @return void
|
|
**/
|
|
function post_updateItem($history = 1) {
|
|
}
|
|
|
|
|
|
/**
|
|
* Actions done before the UPDATE of the item in the database
|
|
*
|
|
* @return void
|
|
**/
|
|
function pre_updateInDB() {
|
|
}
|
|
|
|
|
|
/**
|
|
* Delete an item in the database.
|
|
*
|
|
* @param array $input the _POST vars returned by the item form when press delete
|
|
* @param boolean $force force deletion (default 0)
|
|
* @param boolean $history do history log ? (default 1)
|
|
*
|
|
* @return boolean true on success
|
|
**/
|
|
function delete(array $input, $force = 0, $history = 1) {
|
|
global $DB;
|
|
|
|
if ($DB->isSlave()) {
|
|
return false;
|
|
}
|
|
|
|
if (!$this->getFromDB($input[static::getIndexName()])) {
|
|
return false;
|
|
}
|
|
|
|
// Force purge for templates / may not to be deleted / not dynamic lockable items
|
|
if ($this->isTemplate()
|
|
|| !$this->maybeDeleted()
|
|
// Do not take into account deleted field if maybe dynamic but not dynamic
|
|
|| ($this->useDeletedToLockIfDynamic()
|
|
&& !$this->isDynamic())) {
|
|
$force = 1;
|
|
}
|
|
|
|
// Store input in the object to be available in all sub-method / hook
|
|
$this->input = $input;
|
|
|
|
if (isset($this->input['purge'])) {
|
|
$this->input['_purge'] = $this->input['purge'];
|
|
unset($this->input['purge']);
|
|
} else if ($force) {
|
|
$this->input['_purge'] = 1;
|
|
$this->input['_no_message'] = $this->input['_no_message'] ?? 1;
|
|
}
|
|
|
|
if (isset($this->input['delete'])) {
|
|
$this->input['_delete'] = $this->input['delete'];
|
|
unset($this->input['delete']);
|
|
} else if (!$force) {
|
|
$this->input['_delete'] = 1;
|
|
$this->input['_no_message'] = $this->input['_no_message'] ?? 1;
|
|
}
|
|
|
|
if (!isset($this->input['_no_history'])) {
|
|
$this->input['_no_history'] = !$history;
|
|
}
|
|
|
|
// Purge
|
|
if ($force) {
|
|
Plugin::doHook("pre_item_purge", $this);
|
|
} else {
|
|
Plugin::doHook("pre_item_delete", $this);
|
|
}
|
|
|
|
if (!is_array($this->input)) {
|
|
// $input clear by a hook to cancel delete
|
|
return false;
|
|
}
|
|
|
|
if ($this->pre_deleteItem()) {
|
|
|
|
if ($this->deleteFromDB($force)) {
|
|
|
|
if ($force) {
|
|
$this->addMessageOnPurgeAction();
|
|
$this->post_purgeItem();
|
|
Plugin::doHook("item_purge", $this);
|
|
Impact::clean($this);
|
|
} else {
|
|
$this->addMessageOnDeleteAction();
|
|
|
|
if ($this->dohistory && $history) {
|
|
$changes = [
|
|
0,
|
|
'',
|
|
'',
|
|
];
|
|
$logaction = Log::HISTORY_DELETE_ITEM;
|
|
if ($this->useDeletedToLockIfDynamic()
|
|
&& $this->isDynamic()) {
|
|
$logaction = Log::HISTORY_LOCK_ITEM;
|
|
}
|
|
|
|
Log::history($this->fields["id"], $this->getType(), $changes, 0,
|
|
$logaction);
|
|
}
|
|
$this->post_deleteItem();
|
|
|
|
Plugin::doHook("item_delete", $this);
|
|
}
|
|
if ($this->notificationqueueonaction) {
|
|
QueuedNotification::forceSendFor($this->getType(), $this->fields['id']);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
/**
|
|
* Actions done after the DELETE (mark as deleted) of the item in the database
|
|
*
|
|
* @return void
|
|
**/
|
|
function post_deleteItem() {
|
|
}
|
|
|
|
|
|
/**
|
|
* Actions done after the PURGE of the item in the database
|
|
*
|
|
* @return void
|
|
**/
|
|
function post_purgeItem() {
|
|
}
|
|
|
|
|
|
/**
|
|
* Add a message on delete action
|
|
*
|
|
* @return void
|
|
**/
|
|
function addMessageOnDeleteAction() {
|
|
|
|
if (!$this->maybeDeleted()) {
|
|
return;
|
|
}
|
|
|
|
$addMessAfterRedirect = false;
|
|
if (isset($this->input['_delete'])) {
|
|
$addMessAfterRedirect = true;
|
|
}
|
|
|
|
if (isset($this->input['_no_message'])
|
|
|| !$this->auto_message_on_action) {
|
|
$addMessAfterRedirect = false;
|
|
}
|
|
|
|
if ($addMessAfterRedirect) {
|
|
$link = $this->getFormURL();
|
|
if (!isset($link)) {
|
|
return;
|
|
}
|
|
if (isset($this->input['_no_message_link'])) {
|
|
$display = $this->getNameID();
|
|
} else {
|
|
$display = $this->getLink();
|
|
}
|
|
//TRANS : %s is the description of the updated item
|
|
Session::addMessageAfterRedirect(sprintf(__('%1$s: %2$s'), __('Item successfully deleted'), $display));
|
|
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Add a message on purge action
|
|
*
|
|
* @return void
|
|
**/
|
|
function addMessageOnPurgeAction() {
|
|
|
|
$addMessAfterRedirect = false;
|
|
|
|
if (isset($this->input['_purge'])
|
|
|| isset($this->input['_delete'])) {
|
|
$addMessAfterRedirect = true;
|
|
}
|
|
|
|
if (isset($this->input['_purge'])) {
|
|
$this->input['_no_message_link'] = true;
|
|
}
|
|
|
|
if (isset($this->input['_no_message'])
|
|
|| !$this->auto_message_on_action) {
|
|
$addMessAfterRedirect = false;
|
|
}
|
|
|
|
if ($addMessAfterRedirect) {
|
|
$link = $this->getFormURL();
|
|
if (!isset($link)) {
|
|
return;
|
|
}
|
|
if (isset($this->input['_no_message_link'])) {
|
|
$display = $this->getNameID();
|
|
} else {
|
|
$display = $this->getLink();
|
|
}
|
|
//TRANS : %s is the description of the updated item
|
|
Session::addMessageAfterRedirect(sprintf(__('%1$s: %2$s'), __('Item successfully purged'),
|
|
$display));
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Actions done before the DELETE of the item in the database /
|
|
* Maybe used to add another check for deletion
|
|
*
|
|
* @return boolean true if item need to be deleted else false
|
|
**/
|
|
function pre_deleteItem() {
|
|
return true;
|
|
}
|
|
|
|
|
|
/**
|
|
* Restore an item put in the trashbin in the database.
|
|
*
|
|
* @param array $input the _POST vars returned by the item form when press restore
|
|
* @param boolean $history do history log ? (default 1)
|
|
*
|
|
* @return boolean true on success
|
|
**/
|
|
function restore(array $input, $history = 1) {
|
|
|
|
if (!$this->getFromDB($input[static::getIndexName()])) {
|
|
return false;
|
|
}
|
|
|
|
if (isset($input['restore'])) {
|
|
$input['_restore'] = $input['restore'];
|
|
unset($input['restore']);
|
|
} else {
|
|
$this->input['_restore'] = 1;
|
|
$this->input['_no_message'] = $this->input['_no_message'] ?? 1;
|
|
}
|
|
|
|
// Store input in the object to be available in all sub-method / hook
|
|
$this->input = $input;
|
|
Plugin::doHook("pre_item_restore", $this);
|
|
if (!is_array($this->input)) {
|
|
// $input clear by a hook to cancel retore
|
|
return false;
|
|
}
|
|
|
|
if ($this->restoreInDB()) {
|
|
$this->addMessageOnRestoreAction();
|
|
|
|
if ($this->dohistory && $history) {
|
|
$changes = [
|
|
0,
|
|
'',
|
|
'',
|
|
];
|
|
$logaction = Log::HISTORY_RESTORE_ITEM;
|
|
if ($this->useDeletedToLockIfDynamic()
|
|
&& $this->isDynamic()) {
|
|
$logaction = Log::HISTORY_UNLOCK_ITEM;
|
|
}
|
|
Log::history($this->input["id"], $this->getType(), $changes, 0, $logaction);
|
|
}
|
|
|
|
$this->post_restoreItem();
|
|
Plugin::doHook("item_restore", $this);
|
|
if ($this->notificationqueueonaction) {
|
|
QueuedNotification::forceSendFor($this->getType(), $this->fields['id']);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
/**
|
|
* Actions done after the restore of the item
|
|
*
|
|
* @return void
|
|
**/
|
|
function post_restoreItem() {
|
|
}
|
|
|
|
|
|
/**
|
|
* Add a message on restore action
|
|
*
|
|
* @return void
|
|
**/
|
|
function addMessageOnRestoreAction() {
|
|
|
|
$addMessAfterRedirect = false;
|
|
if (isset($this->input['_restore'])) {
|
|
$addMessAfterRedirect = true;
|
|
}
|
|
|
|
if (isset($this->input['_no_message'])
|
|
|| !$this->auto_message_on_action) {
|
|
$addMessAfterRedirect = false;
|
|
}
|
|
|
|
if ($addMessAfterRedirect) {
|
|
$link = $this->getFormURL();
|
|
if (!isset($link)) {
|
|
return;
|
|
}
|
|
if (isset($this->input['_no_message_link'])) {
|
|
$display = $this->getNameID();
|
|
} else {
|
|
$display = $this->getLink();
|
|
}
|
|
//TRANS : %s is the description of the updated item
|
|
Session::addMessageAfterRedirect(sprintf(__('%1$s: %2$s'), __('Item successfully restored'), $display));
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Reset fields of the item
|
|
*
|
|
* @return void
|
|
**/
|
|
function reset() {
|
|
$this->fields = [];
|
|
}
|
|
|
|
|
|
/**
|
|
* Have I the global right to add an item for the Object
|
|
* May be overloaded if needed (ex Ticket)
|
|
*
|
|
* @since 0.83
|
|
*
|
|
* @param string $type itemtype of object to add
|
|
*
|
|
* @return boolean
|
|
**/
|
|
function canAddItem($type) {
|
|
return $this->can($this->getID(), UPDATE);
|
|
}
|
|
|
|
|
|
/**
|
|
* Have I the right to "create" the Object
|
|
*
|
|
* Default is true and check entity if the objet is entity assign
|
|
*
|
|
* May be overloaded if needed
|
|
*
|
|
* @return boolean
|
|
**/
|
|
function canCreateItem() {
|
|
|
|
if (!$this->checkEntity()) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
/**
|
|
* Have I the right to "update" the Object
|
|
*
|
|
* Default is true and check entity if the objet is entity assign
|
|
*
|
|
* May be overloaded if needed
|
|
*
|
|
* @return boolean
|
|
**/
|
|
function canUpdateItem() {
|
|
|
|
if (!$this->checkEntity()) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
/**
|
|
* Have I the right to "delete" the Object
|
|
*
|
|
* Default is true and check entity if the objet is entity assign
|
|
*
|
|
* May be overloaded if needed
|
|
*
|
|
* @return boolean
|
|
**/
|
|
function canDeleteItem() {
|
|
|
|
if (!$this->checkEntity()) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
/**
|
|
* Have I the right to "purge" the Object
|
|
*
|
|
* Default is true and check entity if the objet is entity assign
|
|
*
|
|
* @since 0.85
|
|
*
|
|
* @return boolean
|
|
**/
|
|
function canPurgeItem() {
|
|
|
|
if (!$this->checkEntity()) {
|
|
return false;
|
|
}
|
|
|
|
// Can purge an object with Infocom only if can purge Infocom
|
|
if (Infocom::canApplyOn($this)) {
|
|
$infocom = new Infocom();
|
|
|
|
if ($infocom->getFromDBforDevice($this->getType(), $this->fields['id'])) {
|
|
return $infocom->canPurge();
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
/**
|
|
* Have I the right to "view" the Object
|
|
* May be overloaded if needed
|
|
*
|
|
* @return boolean
|
|
**/
|
|
function canViewItem() {
|
|
|
|
if (!$this->checkEntity(true)) {
|
|
return false;
|
|
}
|
|
|
|
// else : Global item
|
|
return true;
|
|
}
|
|
|
|
|
|
/**
|
|
* Have i right to see action button
|
|
*
|
|
* @param integer $ID ID to check
|
|
*
|
|
* @since 0.85
|
|
*
|
|
* @return boolean
|
|
**/
|
|
function canEdit($ID) {
|
|
|
|
if ($this->maybeDeleted()) {
|
|
return ($this->can($ID, CREATE)
|
|
|| $this->can($ID, UPDATE)
|
|
|| $this->can($ID, DELETE)
|
|
|| $this->can($ID, PURGE));
|
|
}
|
|
return ($this->can($ID, CREATE)
|
|
|| $this->can($ID, UPDATE)
|
|
|| $this->can($ID, PURGE));
|
|
}
|
|
|
|
|
|
/**
|
|
* Can I change recursive flag to false
|
|
* check if there is "linked" object in another entity
|
|
*
|
|
* May be overloaded if needed
|
|
*
|
|
* @return boolean
|
|
**/
|
|
function canUnrecurs() {
|
|
global $DB;
|
|
|
|
$ID = $this->fields['id'];
|
|
if (($ID < 0)
|
|
|| !$this->fields['is_recursive']) {
|
|
return true;
|
|
}
|
|
|
|
$entities = getAncestorsOf('glpi_entities', $this->fields['entities_id']);
|
|
$entities[] = $this->fields['entities_id'];
|
|
$RELATION = getDbRelations();
|
|
|
|
if ($this instanceof CommonTreeDropdown) {
|
|
$f = getForeignKeyFieldForTable($this->getTable());
|
|
|
|
if (countElementsInTable($this->getTable(),
|
|
[ $f => $ID, 'NOT' => [ 'entities_id' => $entities ]]) > 0) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (isset($RELATION[$this->getTable()])) {
|
|
foreach ($RELATION[$this->getTable()] as $tablename => $field) {
|
|
if ($tablename[0] != '_') {
|
|
|
|
$itemtype = getItemTypeForTable($tablename);
|
|
$item = new $itemtype();
|
|
|
|
if ($item->isEntityAssign()) {
|
|
|
|
// 1->N Relation
|
|
if (is_array($field)) {
|
|
foreach ($field as $f) {
|
|
if (countElementsInTable($tablename,
|
|
[ $f => $ID, 'NOT' => [ 'entities_id' => $entities ]]) > 0) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
} else {
|
|
if (countElementsInTable($tablename,
|
|
[ $field => $ID, 'NOT' => [ 'entities_id' => $entities ]]) > 0) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
} else {
|
|
foreach ($RELATION as $othertable => $rel) {
|
|
// Search for a N->N Relation with devices
|
|
if (($othertable == "_virtual_device")
|
|
&& isset($rel[$tablename])) {
|
|
$devfield = $rel[$tablename][0]; // items_id...
|
|
$typefield = $rel[$tablename][1]; // itemtype...
|
|
|
|
$iterator = $DB->request([
|
|
'SELECT' => $typefield,
|
|
'DISTINCT' => true,
|
|
'FROM' => $tablename,
|
|
'WHERE' => [$field => $ID]
|
|
]);
|
|
|
|
// Search linked device of each type
|
|
while ($data = $iterator->next()) {
|
|
$itemtype = $data[$typefield];
|
|
$itemtable = getTableForItemType($itemtype);
|
|
$item = new $itemtype();
|
|
|
|
if ($item->isEntityAssign()) {
|
|
if (countElementsInTable([$tablename, $itemtable],
|
|
["$tablename.$field" => $ID,
|
|
"$tablename.$typefield" => $itemtype,
|
|
'FKEY' => [$tablename => $devfield, $itemtable => 'id'],
|
|
'NOT' => [$itemtable.'.entities_id' => $entities ]]) > '0') {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
} else if (($othertable != $this->getTable())
|
|
&& isset($rel[$tablename])) {
|
|
|
|
// Search for another N->N Relation
|
|
$itemtype = getItemTypeForTable($othertable);
|
|
$item = new $itemtype();
|
|
|
|
if ($item->isEntityAssign()) {
|
|
if (is_array($rel[$tablename])) {
|
|
foreach ($rel[$tablename] as $otherfield) {
|
|
if (countElementsInTable([$tablename, $othertable],
|
|
["$tablename.$field" => $ID,
|
|
'FKEY' => [$tablename => $otherfield, $othertable => 'id'],
|
|
'NOT' => [$othertable.'.entities_id' => $entities ]]) > '0') {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
} else {
|
|
$otherfield = $rel[$tablename];
|
|
if (countElementsInTable([$tablename, $othertable],
|
|
["$tablename.$field" => $ID,
|
|
'FKEY' => [$tablename => $otherfield, $othertable =>'id'],
|
|
'NOT' => [ $othertable.'.entities_id' => $entities ]]) > '0') {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Doc links to this item
|
|
if (($this->getType() > 0)
|
|
&& countElementsInTable(['glpi_documents_items', 'glpi_documents'],
|
|
['glpi_documents_items.items_id'=> $ID,
|
|
'glpi_documents_items.itemtype'=> $this->getType(),
|
|
'FKEY' => ['glpi_documents_items' => 'documents_id','glpi_documents' => 'id'],
|
|
'NOT' => ['glpi_documents.entities_id' => $entities]]) > '0') {
|
|
return false;
|
|
}
|
|
// TODO : do we need to check all relations in $RELATION["_virtual_device"] for this item
|
|
|
|
// check connections of a computer
|
|
$connectcomputer = ['Monitor', 'Peripheral', 'Phone', 'Printer'];
|
|
if (in_array($this->getType(), $connectcomputer)) {
|
|
return Computer_Item::canUnrecursSpecif($this, $entities);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
/**
|
|
* check if this action can be done on this field of this item by massive actions
|
|
*
|
|
* @since 0.83
|
|
*
|
|
* @param string $action name of the action
|
|
* @param integer $field id of the field
|
|
* @param string $value value of the field
|
|
*
|
|
* @return boolean
|
|
**/
|
|
function canMassiveAction($action, $field, $value) {
|
|
return true;
|
|
}
|
|
|
|
|
|
/**
|
|
* @since 9.1
|
|
*
|
|
* @param array $options Options
|
|
*
|
|
* @return boolean
|
|
**/
|
|
function showDates($options = []) {
|
|
|
|
$isNewID = ((isset($options['withtemplate']) && ($options['withtemplate'] == 2))
|
|
|| $this->isNewID($this->getID()));
|
|
|
|
if ($isNewID) {
|
|
return true;
|
|
}
|
|
|
|
$date_creation_exists = ($this->getField('date_creation') != NOT_AVAILABLE);
|
|
$date_mod_exists = ($this->getField('date_mod') != NOT_AVAILABLE);
|
|
|
|
$colspan = $options['colspan'];
|
|
if ((!isset($options['withtemplate']) || ($options['withtemplate'] == 0))
|
|
&& !empty($this->fields['template_name'])) {
|
|
$colspan = 1;
|
|
}
|
|
|
|
echo "<tr class='tab_bg_1 footerRow'>";
|
|
//Display when it's not a new asset being created
|
|
if ($date_creation_exists
|
|
&& $this->getID() > 0
|
|
&& (!isset($options['withtemplate']) || $options['withtemplate'] == 0)) {
|
|
echo "<th colspan='$colspan'>";
|
|
printf(__('Created on %s'), Html::convDateTime($this->fields["date_creation"]));
|
|
echo "</th>";
|
|
} else if (!isset($options['withtemplate']) || $options['withtemplate'] == 0) {
|
|
echo "<th colspan='$colspan'>";
|
|
echo "</th>";
|
|
}
|
|
|
|
if (isset($options['withtemplate']) && $options['withtemplate']) {
|
|
echo "<th colspan='$colspan'>";
|
|
//TRANS: %s is the datetime of insertion
|
|
printf(__('Created on %s'), Html::convDateTime($_SESSION["glpi_currenttime"]));
|
|
echo "</th>";
|
|
}
|
|
|
|
if ($date_mod_exists) {
|
|
echo "<th colspan='$colspan'>";
|
|
//TRANS: %s is the datetime of update
|
|
printf(__('Last update on %s'), Html::convDateTime($this->fields["date_mod"]));
|
|
echo "</th>";
|
|
} else {
|
|
echo "<th colspan='$colspan'>";
|
|
echo "</th>";
|
|
}
|
|
|
|
if ((!isset($options['withtemplate']) || ($options['withtemplate'] == 0))
|
|
&& !empty($this->fields['template_name'])) {
|
|
echo "<th colspan='".($colspan * 2)."'>";
|
|
printf(__('Created from the template %s'), $this->fields['template_name']);
|
|
echo "</th>";
|
|
}
|
|
|
|
echo "</tr>";
|
|
}
|
|
|
|
/**
|
|
* Display a 2 columns Footer for Form buttons
|
|
* Close the form is user can edit
|
|
*
|
|
* @param array $options array of possible options:
|
|
* - withtemplate : 1 for newtemplate, 2 for newobject from template
|
|
* - colspan for each column (default 2)
|
|
* - candel : set to false to hide "delete" button
|
|
* - canedit : set to false to hide all buttons
|
|
* - addbuttons : array of buttons to add
|
|
*
|
|
* @return void
|
|
**/
|
|
function showFormButtons($options = []) {
|
|
|
|
// for single object like config
|
|
if (isset($this->fields['id'])) {
|
|
$ID = $this->fields['id'];
|
|
} else {
|
|
$ID = 1;
|
|
}
|
|
|
|
$params = [
|
|
'colspan' => 2,
|
|
'withtemplate' => '',
|
|
'candel' => true,
|
|
'canedit' => true,
|
|
'addbuttons' => [],
|
|
'formfooter' => null,
|
|
];
|
|
|
|
if (is_array($options) && count($options)) {
|
|
foreach ($options as $key => $val) {
|
|
$params[$key] = $val;
|
|
}
|
|
}
|
|
|
|
Plugin::doHook("post_item_form", ['item' => $this, 'options' => &$params]);
|
|
|
|
if ($params['formfooter'] === null) {
|
|
$this->showDates($params);
|
|
}
|
|
|
|
if (!$params['canedit']
|
|
|| !$this->canEdit($ID)) {
|
|
echo "</table></div>";
|
|
// Form Header always open form
|
|
Html::closeForm();
|
|
return false;
|
|
}
|
|
|
|
echo "<tr class='tab_bg_2'>";
|
|
|
|
if ($params['withtemplate']
|
|
||$this->isNewID($ID)) {
|
|
|
|
echo "<td class='center' colspan='".($params['colspan']*2)."'>";
|
|
|
|
if (($ID <= 0) || ($params['withtemplate'] == 2)) {
|
|
echo Html::submit(
|
|
"<i class='fas fa-plus'></i> "._x('button', 'Add'),
|
|
['name' => 'add']
|
|
);
|
|
} else {
|
|
//TRANS : means update / actualize
|
|
echo Html::submit(
|
|
"<i class='fas fa-save'></i> "._x('button', 'Save'),
|
|
['name' => 'update']
|
|
);
|
|
}
|
|
|
|
} else {
|
|
if ($params['candel']
|
|
&& !$this->can($ID, DELETE)
|
|
&& !$this->can($ID, PURGE)) {
|
|
$params['candel'] = false;
|
|
}
|
|
|
|
if ($params['canedit'] && $this->can($ID, UPDATE)) {
|
|
echo "<td class='center' colspan='".($params['colspan']*2)."'>\n";
|
|
echo Html::submit(
|
|
"<i class='fas fa-save'></i> "._x('button', 'Save'),
|
|
['name' => 'update']
|
|
);
|
|
}
|
|
|
|
if ($params['candel']) {
|
|
if ($params['canedit'] && $this->can($ID, UPDATE)) {
|
|
echo "</td></tr><tr class='tab_bg_2'>\n";
|
|
}
|
|
if ($this->isDeleted()) {
|
|
if ($this->can($ID, DELETE)) {
|
|
echo "<td class='right' colspan='".($params['colspan']*2)."' >\n";
|
|
echo Html::submit(
|
|
"<i class='fas fa-trash-restore'></i> "._x('button', 'Restore'),
|
|
['name' => 'restore']
|
|
);
|
|
}
|
|
|
|
if ($this->can($ID, PURGE)) {
|
|
echo "<span class='very_small_space'>";
|
|
if (in_array($this->getType(), Item_Devices::getConcernedItems())) {
|
|
Html::showToolTip(__('Check to keep the devices while deleting this item'));
|
|
echo " ";
|
|
echo "<input type='checkbox' name='keep_devices' value='1'";
|
|
if (!empty($_SESSION['glpikeep_devices_when_purging_item'])) {
|
|
echo " checked";
|
|
}
|
|
echo "> ";
|
|
}
|
|
echo Html::submit(
|
|
"<i class='fas fa-trash-alt'></i> "._x('button', 'Delete permanently'),
|
|
['name' => 'purge']
|
|
);
|
|
echo "</span>";
|
|
}
|
|
|
|
} else {
|
|
echo "<td class='right' colspan='".($params['colspan']*2)."' >\n";
|
|
// If maybe dynamic : do not take into account is_deleted field
|
|
if (!$this->maybeDeleted()
|
|
|| $this->useDeletedToLockIfDynamic()) {
|
|
if ($this->can($ID, PURGE)) {
|
|
echo Html::submit(
|
|
"<i class='fas fa-trash-alt'></i> "._x('button', 'Delete permanently'),
|
|
[
|
|
'name' => 'purge',
|
|
'confirm' => __('Confirm the final deletion?')
|
|
]
|
|
);
|
|
}
|
|
} else if (!$this->isDeleted()
|
|
&& $this->can($ID, DELETE)) {
|
|
echo Html::submit(
|
|
"<i class='fas fa-trash-alt'></i> "._x('button', 'Put in trashbin'),
|
|
['name' => 'delete']
|
|
);
|
|
}
|
|
}
|
|
|
|
}
|
|
if ($this->isField('date_mod')) {
|
|
echo "<input type='hidden' name='_read_date_mod' value='".$this->getField('date_mod')."'>";
|
|
}
|
|
}
|
|
|
|
if (!$this->isNewID($ID)) {
|
|
echo "<input type='hidden' name='id' value='$ID'>";
|
|
}
|
|
echo "</td>";
|
|
echo "</tr>\n";
|
|
|
|
if ($params['canedit']
|
|
&& count($params['addbuttons'])) {
|
|
echo "<tr class='tab_bg_2'>";
|
|
echo "<td class='right' colspan='".($params['colspan']*2)."'>";
|
|
foreach ($params['addbuttons'] as $key => $val) {
|
|
echo "<button type='submit' class='vsubmit' name='$key' value='1'>
|
|
$val
|
|
</button> ";
|
|
}
|
|
echo "</td>";
|
|
echo "</tr>";
|
|
}
|
|
|
|
// Close for Form
|
|
echo "</table></div>";
|
|
Html::closeForm();
|
|
}
|
|
|
|
|
|
/**
|
|
* Initialize item and check right before managing the edit form
|
|
*
|
|
* @since 0.84
|
|
*
|
|
* @param integer $ID ID of the item/template
|
|
* @param array $options Array of possible options:
|
|
* - withtemplate : 1 for newtemplate, 2 for newobject from template
|
|
*
|
|
* @return integer|void value of withtemplate option (exit of no right)
|
|
**/
|
|
function initForm($ID, Array $options = []) {
|
|
|
|
if (isset($options['withtemplate'])
|
|
&& ($options['withtemplate'] == 2)
|
|
&& !$this->isNewID($ID)) {
|
|
// Create item from template
|
|
// Check read right on the template
|
|
$this->check($ID, READ);
|
|
|
|
// Restore saved input or template data
|
|
$input = $this->restoreInput($this->fields);
|
|
|
|
// If entity assign force current entity to manage recursive templates
|
|
if ($this->isEntityAssign()) {
|
|
$input['entities_id'] = $_SESSION['glpiactive_entity'];
|
|
}
|
|
|
|
// Check create right
|
|
$this->check(-1, CREATE, $input);
|
|
|
|
} else if ($this->isNewID($ID)) {
|
|
// Restore saved input if available
|
|
$input = $this->restoreInput($options);
|
|
// Create item
|
|
$this->check(-1, CREATE, $input);
|
|
} else {
|
|
// Existing item
|
|
$this->check($ID, READ);
|
|
}
|
|
|
|
return (isset($options['withtemplate']) ? $options['withtemplate'] : '');
|
|
}
|
|
|
|
|
|
/**
|
|
*
|
|
* Display a 2 columns Header 1 for ID, 1 for recursivity menu
|
|
* Open the form is user can edit
|
|
*
|
|
* @param array $options array of possible options:
|
|
* - target for the Form
|
|
* - withtemplate : 1 for newtemplate, 2 for newobject from template
|
|
* - colspan for each column (default 2)
|
|
* - formoptions string (javascript p.e.)
|
|
* - canedit boolean edit mode of form ?
|
|
* - formtitle specific form title
|
|
* - noid Set to true if ID should not be append (eg. already done in formtitle)
|
|
*
|
|
* @return void
|
|
**/
|
|
function showFormHeader($options = []) {
|
|
|
|
$ID = $this->fields['id'];
|
|
|
|
$params = [
|
|
'target' => $this->getFormURL(),
|
|
'colspan' => 2,
|
|
'withtemplate' => '',
|
|
'formoptions' => '',
|
|
'canedit' => true,
|
|
'formtitle' => null,
|
|
'noid' => false
|
|
];
|
|
|
|
if (is_array($options) && count($options)) {
|
|
foreach ($options as $key => $val) {
|
|
$params[$key] = $val;
|
|
}
|
|
}
|
|
|
|
// Template case : clean entities data
|
|
if (($params['withtemplate'] == 2)
|
|
&& $this->isEntityAssign()) {
|
|
$this->fields['entities_id'] = $_SESSION['glpiactive_entity'];
|
|
}
|
|
|
|
$rand = mt_rand();
|
|
if ($this->canEdit($ID)) {
|
|
echo "<form name='form' method='post' action='".$params['target']."' ".
|
|
$params['formoptions']." enctype=\"multipart/form-data\">";
|
|
|
|
//Should add an hidden entities_id field ?
|
|
//If the table has an entities_id field
|
|
if ($this->isField("entities_id")) {
|
|
//The object type can be assigned to an entity
|
|
if ($this->isEntityAssign()) {
|
|
if (isset($params['entities_id'])) {
|
|
$entity = $this->fields['entities_id'] = $params['entities_id'];
|
|
|
|
} else if ($this->isNewID($ID)
|
|
|| ($params['withtemplate'] == 2)) {
|
|
//It's a new object to be added
|
|
$entity = $_SESSION['glpiactive_entity'];
|
|
|
|
} else {
|
|
//It's an existing object to be displayed
|
|
$entity = $this->fields['entities_id'];
|
|
}
|
|
|
|
echo "<input type='hidden' name='entities_id' value='$entity'>";
|
|
|
|
} else if ($this->getType() != 'User') {
|
|
// For Rules except ruleticket and slalevel
|
|
echo "<input type='hidden' name='entities_id' value='0'>";
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
echo "<div class='spaced' id='tabsbody'>";
|
|
echo "<table class='tab_cadre_fixe' id='mainformtable'>";
|
|
|
|
if ($params['formtitle'] !== '' && $params['formtitle'] !== false) {
|
|
echo "<tr class='headerRow'><th colspan='".$params['colspan']."'>";
|
|
|
|
if (!empty($params['withtemplate']) && ($params['withtemplate'] == 2)
|
|
&& !$this->isNewID($ID)) {
|
|
|
|
echo "<input type='hidden' name='template_name' value='".$this->fields["template_name"]."'>";
|
|
|
|
//TRANS: %s is the template name
|
|
printf(__('Created from the template %s'), $this->fields["template_name"]);
|
|
|
|
} else if (!empty($params['withtemplate']) && ($params['withtemplate'] == 1)) {
|
|
echo "<input type='hidden' name='is_template' value='1'>\n";
|
|
echo "<label for='textfield_template_name$rand'>" . __('Template name') . "</label>";
|
|
Html::autocompletionTextField(
|
|
$this,
|
|
'template_name',
|
|
[
|
|
'size' => 25,
|
|
'required' => true,
|
|
'rand' => $rand
|
|
]
|
|
);
|
|
} else if ($this->isNewID($ID)) {
|
|
$nametype = $params['formtitle'] !== null ? $params['formtitle'] : $this->getTypeName(1);
|
|
printf(__('%1$s - %2$s'), __('New item'), $nametype);
|
|
} else {
|
|
$nametype = $params['formtitle'] !== null ? $params['formtitle'] : $this->getTypeName(1);
|
|
if (!$params['noid'] && ($_SESSION['glpiis_ids_visible'] || empty($nametype))) {
|
|
//TRANS: %1$s is the Itemtype name and $2$d the ID of the item
|
|
$nametype = sprintf(__('%1$s - ID %2$d'), $nametype, $ID);
|
|
}
|
|
echo $nametype;
|
|
}
|
|
$entityname = '';
|
|
if (isset($this->fields["entities_id"])
|
|
&& Session::isMultiEntitiesMode()
|
|
&& $this->isEntityAssign()) {
|
|
$entityname = Dropdown::getDropdownName("glpi_entities", $this->fields["entities_id"]);
|
|
}
|
|
|
|
echo "</th><th colspan='".$params['colspan']."'>";
|
|
if (get_class($this) != 'Entity') {
|
|
if ($this->maybeRecursive()) {
|
|
if (Session::isMultiEntitiesMode()) {
|
|
echo "<table class='tab_format'><tr class='headerRow responsive_hidden'><th>".$entityname."</th>";
|
|
echo "<th class='right'><label for='dropdown_is_recursive$rand'>".__('Child entities')."</label></th><th>";
|
|
if ($params['canedit']) {
|
|
if ($this instanceof CommonDBChild) {
|
|
echo Dropdown::getYesNo($this->isRecursive());
|
|
if (isset($this->fields["is_recursive"])) {
|
|
echo "<input type='hidden' name='is_recursive' value='".$this->fields["is_recursive"]."'>";
|
|
}
|
|
$comment = __("Can't change this attribute. It's inherited from its parent.");
|
|
// CommonDBChild : entity data is get or copy from parent
|
|
|
|
} else if (!$this->can($ID, 'recursive')) {
|
|
echo Dropdown::getYesNo($this->fields["is_recursive"]);
|
|
$comment = __('You are not allowed to change the visibility flag for child entities.');
|
|
|
|
} else if (!$this->canUnrecurs()) {
|
|
echo Dropdown::getYesNo($this->fields["is_recursive"]);
|
|
$comment = __('Flag change forbidden. Linked items found.');
|
|
|
|
} else {
|
|
Dropdown::showYesNo("is_recursive", $this->fields["is_recursive"], -1, ['rand' => $rand]);
|
|
$comment = __('Change visibility in child entities');
|
|
}
|
|
echo " ";
|
|
Html::showToolTip($comment);
|
|
} else {
|
|
echo Dropdown::getYesNo($this->fields["is_recursive"]);
|
|
}
|
|
echo "</th></tr></table>";
|
|
} else {
|
|
echo $entityname;
|
|
echo "<input type='hidden' name='is_recursive' value='0'>";
|
|
}
|
|
} else {
|
|
echo $entityname;
|
|
}
|
|
}
|
|
echo "</th></tr>\n";
|
|
}
|
|
|
|
Plugin::doHook("pre_item_form", ['item' => $this, 'options' => &$params]);
|
|
|
|
// If in modal : do not display link on message after redirect
|
|
if (isset($_REQUEST['_in_modal']) && $_REQUEST['_in_modal']) {
|
|
echo "<input type='hidden' name='_no_message_link' value='1'>";
|
|
}
|
|
|
|
}
|
|
|
|
|
|
/**
|
|
* is the parameter ID must be considered as new one ?
|
|
* Default is empty of <0 may be overriden (for entity for example)
|
|
*
|
|
* @param integer $ID ID of the item (-1 if new item)
|
|
*
|
|
* @return boolean
|
|
**/
|
|
static function isNewID($ID) {
|
|
return (empty($ID) || ($ID <= 0));
|
|
}
|
|
|
|
|
|
/**
|
|
* is the current object a new one
|
|
*
|
|
* @since 0.83
|
|
*
|
|
* @return boolean
|
|
**/
|
|
function isNewItem() {
|
|
|
|
if (isset($this->fields['id'])) {
|
|
return $this->isNewID($this->fields['id']);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
/**
|
|
* Check right on an item
|
|
*
|
|
* @param integer $ID ID of the item (-1 if new item)
|
|
* @param mixed $right Right to check : r / w / recursive / READ / UPDATE / DELETE
|
|
* @param array $input array of input data (used for adding item) (default NULL)
|
|
*
|
|
* @return boolean
|
|
**/
|
|
function can($ID, $right, array &$input = null) {
|
|
// Clean ID :
|
|
$ID = Toolbox::cleanInteger($ID);
|
|
|
|
// Create process
|
|
if ($this->isNewID($ID)) {
|
|
if (!isset($this->fields['id'])) {
|
|
// Only once
|
|
$this->getEmpty();
|
|
}
|
|
|
|
if (is_array($input)) {
|
|
$input = $this->addNeededInfoToInput($input);
|
|
// Copy input field to allow getEntityID() to work
|
|
// from entites_id field or from parent item ref
|
|
foreach ($input as $key => $val) {
|
|
if (isset($this->fields[$key])) {
|
|
$this->fields[$key] = $val;
|
|
}
|
|
}
|
|
// Store to be available for others functions
|
|
$this->input = $input;
|
|
}
|
|
|
|
if ($this->isPrivate()
|
|
&& ($this->fields['users_id'] == Session::getLoginUserID())) {
|
|
return true;
|
|
}
|
|
return (static::canCreate() && $this->canCreateItem());
|
|
|
|
}
|
|
// else : Get item if not already loaded
|
|
if (!isset($this->fields['id']) || ($this->fields['id'] != $ID)) {
|
|
// Item not found : no right
|
|
if (!$this->getFromDB($ID)) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/* Hook to restrict user right on current item @since 9.2 */
|
|
$this->right = $right;
|
|
Plugin::doHook("item_can", $this);
|
|
if ($this->right !== $right) {
|
|
return false;
|
|
}
|
|
unset($this->right);
|
|
|
|
switch ($right) {
|
|
case READ :
|
|
// Personnal item
|
|
if ($this->isPrivate()
|
|
&& ($this->fields['users_id'] === Session::getLoginUserID())) {
|
|
return true;
|
|
}
|
|
return (static::canView() && $this->canViewItem());
|
|
|
|
case UPDATE :
|
|
// Personnal item
|
|
if ($this->isPrivate()
|
|
&& ($this->fields['users_id'] === Session::getLoginUserID())) {
|
|
return true;
|
|
}
|
|
return (static::canUpdate() && $this->canUpdateItem());
|
|
|
|
case DELETE :
|
|
// Personnal item
|
|
if ($this->isPrivate()
|
|
&& ($this->fields['users_id'] === Session::getLoginUserID())) {
|
|
return true;
|
|
}
|
|
return (static::canDelete() && $this->canDeleteItem());
|
|
|
|
case PURGE :
|
|
// Personnal item
|
|
if ($this->isPrivate()
|
|
&& ($this->fields['users_id'] === Session::getLoginUserID())) {
|
|
return true;
|
|
}
|
|
return (static::canPurge() && $this->canPurgeItem());
|
|
|
|
case CREATE :
|
|
// Personnal item
|
|
if ($this->isPrivate()
|
|
&& ($this->fields['users_id'] === Session::getLoginUserID())) {
|
|
return true;
|
|
}
|
|
return (static::canCreate() && $this->canCreateItem());
|
|
|
|
case 'recursive' :
|
|
if ($this->isEntityAssign()
|
|
&& $this->maybeRecursive()) {
|
|
if (static::canCreate()
|
|
&& Session::haveAccessToEntity($this->getEntityID())) {
|
|
// Can make recursive if recursive access to entity
|
|
return Session::haveRecursiveAccessToEntity($this->getEntityID());
|
|
}
|
|
}
|
|
break;
|
|
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
/**
|
|
* Check right on an item with block
|
|
*
|
|
* @param integer $ID ID of the item (-1 if new item)
|
|
* @param mixed $right Right to check : r / w / recursive
|
|
* @param array $input array of input data (used for adding item) (default NULL)
|
|
*
|
|
* @return void
|
|
**/
|
|
function check($ID, $right, array &$input = null) {
|
|
|
|
// Check item exists
|
|
if (!$this->isNewID($ID)
|
|
&& (!isset($this->fields['id']) || $this->fields['id'] != $ID)
|
|
&& !$this->getFromDB($ID)) {
|
|
// Gestion timeout session
|
|
Session::redirectIfNotLoggedIn();
|
|
Html::displayNotFoundError();
|
|
|
|
} else {
|
|
if (!$this->can($ID, $right, $input)) {
|
|
// Gestion timeout session
|
|
Session::redirectIfNotLoggedIn();
|
|
Html::displayRightError();
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Check if have right on this entity
|
|
*
|
|
* @param boolean $recursive set true to accept recursive items of ancestors
|
|
* of active entities (View case for example) (default false)
|
|
* @since 0.85
|
|
*
|
|
* @return boolean
|
|
**/
|
|
function checkEntity($recursive = false) {
|
|
|
|
// Is an item assign to an entity
|
|
if ($this->isEntityAssign()) {
|
|
// Can be recursive check
|
|
if ($recursive && $this->maybeRecursive()) {
|
|
return Session::haveAccessToEntity($this->getEntityID(), $this->isRecursive());
|
|
}
|
|
// else : No recursive item // Have access to entity
|
|
return Session::haveAccessToEntity($this->getEntityID());
|
|
}
|
|
// else : Global item
|
|
return true;
|
|
}
|
|
|
|
|
|
/**
|
|
* Check global right on an object
|
|
*
|
|
* @param mixed $right Right to check : c / r / w / d
|
|
*
|
|
* @return void
|
|
**/
|
|
function checkGlobal($right) {
|
|
|
|
if (!$this->canGlobal($right)) {
|
|
// Gestion timeout session
|
|
Session::redirectIfNotLoggedIn();
|
|
Html::displayRightError();
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Get global right on an object
|
|
*
|
|
* @param mixed $right Right to check : c / r / w / d / READ / UPDATE / CREATE / DELETE
|
|
*
|
|
* @return void
|
|
**/
|
|
function canGlobal($right) {
|
|
|
|
switch ($right) {
|
|
case READ :
|
|
return static::canView();
|
|
|
|
case UPDATE :
|
|
return static::canUpdate();
|
|
|
|
case CREATE :
|
|
return static::canCreate();
|
|
|
|
case DELETE :
|
|
return static::canDelete();
|
|
|
|
case PURGE :
|
|
return static::canPurge();
|
|
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
/**
|
|
* Get the ID of entity assigned to the object
|
|
*
|
|
* Can be overloaded (ex : infocom)
|
|
*
|
|
* @return integer ID of the entity
|
|
**/
|
|
function getEntityID() {
|
|
|
|
if ($this->isEntityAssign()) {
|
|
return $this->fields["entities_id"];
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
|
|
/**
|
|
* Is the object assigned to an entity
|
|
*
|
|
* Can be overloaded (ex : infocom)
|
|
*
|
|
* @return boolean
|
|
**/
|
|
function isEntityAssign() {
|
|
|
|
if (!array_key_exists('id', $this->fields)) {
|
|
$this->getEmpty();
|
|
}
|
|
return array_key_exists('entities_id', $this->fields);
|
|
}
|
|
|
|
|
|
/**
|
|
* Is the object may be recursive
|
|
*
|
|
* Can be overloaded (ex : infocom)
|
|
*
|
|
* @return boolean
|
|
**/
|
|
function maybeRecursive() {
|
|
|
|
if (!array_key_exists('id', $this->fields)) {
|
|
$this->getEmpty();
|
|
}
|
|
return array_key_exists('is_recursive', $this->fields);
|
|
}
|
|
|
|
|
|
/**
|
|
* Is the object recursive
|
|
*
|
|
* Can be overloaded (ex : infocom)
|
|
*
|
|
* @return boolean
|
|
**/
|
|
function isRecursive() {
|
|
|
|
if ($this->maybeRecursive()) {
|
|
return $this->fields["is_recursive"];
|
|
}
|
|
// Return integer value to be used to fill is_recursive field
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**
|
|
* Is the object may be deleted
|
|
*
|
|
* @return boolean
|
|
**/
|
|
function maybeDeleted() {
|
|
|
|
if (!isset($this->fields['id'])) {
|
|
$this->getEmpty();
|
|
}
|
|
return array_key_exists('is_deleted', $this->fields);
|
|
}
|
|
|
|
|
|
/**
|
|
* Is the object deleted
|
|
*
|
|
* @return boolean
|
|
**/
|
|
function isDeleted() {
|
|
|
|
if ($this->maybeDeleted()) {
|
|
return $this->fields["is_deleted"];
|
|
}
|
|
// Return integer value to be used to fill is_deleted field
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
/**
|
|
* Can object be activated
|
|
*
|
|
* @since 9.2
|
|
*
|
|
* @return boolean
|
|
**/
|
|
function maybeActive() {
|
|
|
|
if (!isset($this->fields['id'])) {
|
|
$this->getEmpty();
|
|
}
|
|
return array_key_exists('is_active', $this->fields);
|
|
}
|
|
|
|
|
|
/**
|
|
* Is the object active
|
|
*
|
|
* @since 9.2
|
|
*
|
|
* @return boolean
|
|
**/
|
|
function isActive() {
|
|
|
|
if ($this->maybeActive()) {
|
|
return $this->fields["is_active"];
|
|
}
|
|
// Return integer value to be used to fill is_active field
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
/**
|
|
* Is the object may be a template
|
|
*
|
|
* @return boolean
|
|
**/
|
|
function maybeTemplate() {
|
|
|
|
if (!isset($this->fields['id'])) {
|
|
$this->getEmpty();
|
|
}
|
|
return isset($this->fields['is_template']);
|
|
}
|
|
|
|
|
|
/**
|
|
* Is the object a template
|
|
*
|
|
* @return boolean
|
|
**/
|
|
function isTemplate() {
|
|
|
|
if ($this->maybeTemplate()) {
|
|
return $this->fields["is_template"];
|
|
}
|
|
// Return integer value to be used to fill is_template field
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**
|
|
* Can the object be dynamic
|
|
*
|
|
* @since 0.84
|
|
*
|
|
* @return boolean
|
|
**/
|
|
function maybeDynamic() {
|
|
|
|
if (!isset($this->fields['id'])) {
|
|
$this->getEmpty();
|
|
}
|
|
return array_key_exists('is_dynamic', $this->fields);
|
|
}
|
|
|
|
|
|
/**
|
|
* Use deleted field in case of dynamic management to lock ?
|
|
*
|
|
* need to be overriden if object need to use standard deleted management (Computer...)
|
|
* @since 0.84
|
|
*
|
|
* @return boolean
|
|
**/
|
|
function useDeletedToLockIfDynamic() {
|
|
return $this->maybeDynamic();
|
|
}
|
|
|
|
|
|
/**
|
|
* Is an object dynamic or not
|
|
*
|
|
* @since 0.84
|
|
*
|
|
* @return boolean
|
|
**/
|
|
function isDynamic() {
|
|
|
|
if ($this->maybeDynamic()) {
|
|
return (bool)$this->fields['is_dynamic'];
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
/**
|
|
* Is the object may be private
|
|
*
|
|
* @return boolean
|
|
**/
|
|
function maybePrivate() {
|
|
|
|
if (!isset($this->fields['id'])) {
|
|
$this->getEmpty();
|
|
}
|
|
return (array_key_exists('is_private', $this->fields)
|
|
&& array_key_exists('users_id', $this->fields));
|
|
}
|
|
|
|
|
|
/**
|
|
* Is the object private
|
|
*
|
|
* @return boolean
|
|
**/
|
|
function isPrivate() {
|
|
|
|
if ($this->maybePrivate()) {
|
|
return (bool)$this->fields["is_private"];
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Can object have a location
|
|
*
|
|
* @since 9.3
|
|
*
|
|
* @return boolean
|
|
*/
|
|
function maybeLocated() {
|
|
|
|
if (!array_key_exists('id', $this->fields)) {
|
|
$this->getEmpty();
|
|
}
|
|
return array_key_exists('locations_id', $this->fields);
|
|
}
|
|
|
|
/**
|
|
* Return the linked items (in computers_items)
|
|
*
|
|
* @return array an array of linked items like array('Computer' => array(1,2), 'Printer' => array(5,6))
|
|
* @since 0.84.4
|
|
**/
|
|
function getLinkedItems() {
|
|
return [];
|
|
}
|
|
|
|
|
|
/**
|
|
* Return the count of linked items (in computers_items)
|
|
*
|
|
* @return integer number of linked items
|
|
* @since 0.84.4
|
|
**/
|
|
function getLinkedItemsCount() {
|
|
|
|
$linkeditems = $this->getLinkedItems();
|
|
$nb = 0;
|
|
if (count($linkeditems)) {
|
|
foreach ($linkeditems as $tab) {
|
|
$nb += count($tab);
|
|
}
|
|
}
|
|
return $nb;
|
|
}
|
|
|
|
|
|
/**
|
|
* Return a field Value if exists
|
|
*
|
|
* @param string $field field name
|
|
*
|
|
* @return mixed value of the field / false if not exists
|
|
**/
|
|
function getField($field) {
|
|
|
|
if (array_key_exists($field, $this->fields)) {
|
|
return $this->fields[$field];
|
|
}
|
|
return NOT_AVAILABLE;
|
|
}
|
|
|
|
|
|
/**
|
|
* Determine if a field exists
|
|
*
|
|
* @param string $field field name
|
|
*
|
|
* @return boolean
|
|
**/
|
|
function isField($field) {
|
|
|
|
if (!isset($this->fields['id'])) {
|
|
$this->getEmpty();
|
|
}
|
|
return array_key_exists($field, $this->fields);
|
|
}
|
|
|
|
|
|
/**
|
|
* Get comments of the Object
|
|
*
|
|
* @return string comments of the object in the current language (HTML)
|
|
**/
|
|
function getComments() {
|
|
|
|
$comment = "";
|
|
$toadd = [];
|
|
if ($this->isField('completename')) {
|
|
$toadd[] = ['name' => __('Complete name'),
|
|
'value' => nl2br($this->getField('completename'))];
|
|
}
|
|
|
|
if ($this->isField('serial')) {
|
|
$toadd[] = ['name' => __('Serial number'),
|
|
'value' => nl2br($this->getField('serial'))];
|
|
}
|
|
|
|
if ($this->isField('otherserial')) {
|
|
$toadd[] = ['name' => __('Inventory number'),
|
|
'value' => nl2br($this->getField('otherserial'))];
|
|
}
|
|
|
|
if ($this->isField('states_id') && $this->getType()!='State') {
|
|
$tmp = Dropdown::getDropdownName('glpi_states', $this->getField('states_id'));
|
|
if ((strlen($tmp) != 0) && ($tmp != ' ')) {
|
|
$toadd[] = ['name' => __('Status'),
|
|
'value' => $tmp];
|
|
}
|
|
}
|
|
|
|
if ($this->isField('locations_id') && $this->getType()!='Location') {
|
|
$tmp = Dropdown::getDropdownName("glpi_locations", $this->getField('locations_id'));
|
|
if ((strlen($tmp) != 0) && ($tmp != ' ')) {
|
|
$toadd[] = ['name' => Location::getTypeName(1),
|
|
'value' => $tmp];
|
|
}
|
|
}
|
|
|
|
if ($this->isField('users_id')) {
|
|
$tmp = getUserName($this->getField('users_id'));
|
|
if ((strlen($tmp) != 0) && ($tmp != ' ')) {
|
|
$toadd[] = ['name' => User::getTypeName(1),
|
|
'value' => $tmp];
|
|
}
|
|
}
|
|
|
|
if ($this->isField('groups_id')
|
|
&& ($this->getType() != 'Group')) {
|
|
$tmp = Dropdown::getDropdownName("glpi_groups", $this->getField('groups_id'));
|
|
if ((strlen($tmp) != 0) && ($tmp != ' ')) {
|
|
$toadd[] = ['name' => Group::getTypeName(1),
|
|
'value' => $tmp];
|
|
}
|
|
}
|
|
|
|
if ($this->isField('users_id_tech')) {
|
|
$tmp = getUserName($this->getField('users_id_tech'));
|
|
if ((strlen($tmp) != 0) && ($tmp != ' ')) {
|
|
$toadd[] = ['name' => __('Technician in charge of the hardware'),
|
|
'value' => $tmp];
|
|
}
|
|
}
|
|
|
|
if ($this->isField('contact')) {
|
|
$toadd[] = ['name' => __('Alternate username'),
|
|
'value' => nl2br($this->getField('contact'))];
|
|
}
|
|
|
|
if ($this->isField('contact_num')) {
|
|
$toadd[] = ['name' => __('Alternate username number'),
|
|
'value' => nl2br($this->getField('contact_num'))];
|
|
}
|
|
|
|
if (Infocom::canApplyOn($this)) {
|
|
$infocom = new Infocom();
|
|
if ($infocom->getFromDBforDevice($this->getType(), $this->fields['id'])) {
|
|
$toadd[] = ['name' => __('Warranty expiration date'),
|
|
'value' => Infocom::getWarrantyExpir($infocom->fields["warranty_date"],
|
|
$infocom->fields["warranty_duration"],
|
|
0, true)];
|
|
}
|
|
}
|
|
|
|
if (($this instanceof CommonDropdown)
|
|
&& $this->isField('comment')) {
|
|
$toadd[] = ['name' => __('Comments'),
|
|
'value' => nl2br($this->getField('comment'))];
|
|
}
|
|
|
|
if (count($toadd)) {
|
|
foreach ($toadd as $data) {
|
|
// Do not use SPAN here
|
|
$comment .= sprintf(__('%1$s: %2$s')."<br>",
|
|
"<strong>".$data['name'], "</strong>".$data['value']);
|
|
}
|
|
}
|
|
|
|
if (!empty($comment)) {
|
|
return Html::showToolTip($comment, ['display' => false]);
|
|
}
|
|
|
|
return $comment;
|
|
}
|
|
|
|
|
|
/**
|
|
* @since 0.84
|
|
*
|
|
* Get field used for name
|
|
*
|
|
* @return string
|
|
**/
|
|
static function getNameField() {
|
|
return 'name';
|
|
}
|
|
|
|
|
|
/**
|
|
* @since 0.84
|
|
*
|
|
* Get field used for completename
|
|
*
|
|
* @return string
|
|
**/
|
|
static function getCompleteNameField() {
|
|
return 'completename';
|
|
}
|
|
|
|
|
|
/**
|
|
* Get raw name of the object
|
|
* Maybe overloaded
|
|
*
|
|
* @deprecated 9.5
|
|
* @see CommonDBTM::getNameField
|
|
* @since 0.85
|
|
*
|
|
* @return string
|
|
**/
|
|
function getRawName() {
|
|
\Toolbox::deprecated('Use CommonDBTM::getFriendlyName()');
|
|
|
|
return $this->getFriendlyName();
|
|
}
|
|
|
|
|
|
/** Get raw completename of the object
|
|
* Maybe overloaded
|
|
*
|
|
* @see CommonDBTM::getCompleteNameField
|
|
*
|
|
* @since 0.85
|
|
*
|
|
* @return string
|
|
**/
|
|
function getRawCompleteName() {
|
|
|
|
if (isset($this->fields[static::getCompleteNameField()])) {
|
|
return $this->fields[static::getCompleteNameField()];
|
|
}
|
|
return '';
|
|
}
|
|
|
|
|
|
/**
|
|
* Get the name of the object
|
|
*
|
|
* @param array $options array of options
|
|
* - comments : boolean / display comments
|
|
* - complete : boolean / display completename instead of name
|
|
* - additional : boolean / display aditionals information
|
|
*
|
|
* @return string name of the object in the current language
|
|
*
|
|
* @see CommonDBTM::getRawCompleteName
|
|
* @see CommonDBTM::getFriendlyName
|
|
**/
|
|
function getName($options = []) {
|
|
|
|
$p = [
|
|
'comments' => false,
|
|
'complete' => false,
|
|
'additional' => false,
|
|
];
|
|
|
|
if (is_array($options)) {
|
|
foreach ($options as $key => $val) {
|
|
$p[$key] = $val;
|
|
}
|
|
}
|
|
|
|
$name = '';
|
|
if ($p['complete']) {
|
|
$name = $this->getRawCompleteName();
|
|
}
|
|
if (empty($name)) {
|
|
$name = $this->getFriendlyName();
|
|
}
|
|
|
|
if (strlen($name) != 0) {
|
|
if ($p['additional']) {
|
|
$pre = $this->getPreAdditionalInfosForName();
|
|
if (!empty($pre)) {
|
|
$name = sprintf(__('%1$s - %2$s'), $pre, $name);
|
|
}
|
|
$post = $this->getPostAdditionalInfosForName();
|
|
if (!empty($post)) {
|
|
$name = sprintf(__('%1$s - %2$s'), $name, $post);
|
|
}
|
|
}
|
|
if ($p['comments']) {
|
|
$comment = $this->getComments();
|
|
if (!empty($comment)) {
|
|
$name = sprintf(__('%1$s - %2$s'), $name, $comment);
|
|
}
|
|
}
|
|
return $name;
|
|
}
|
|
return NOT_AVAILABLE;
|
|
}
|
|
|
|
|
|
/**
|
|
* Get additionals information to add before name
|
|
*
|
|
* @since 0.84
|
|
*
|
|
* @return string string to add
|
|
**/
|
|
function getPreAdditionalInfosForName() {
|
|
return '';
|
|
}
|
|
|
|
/**
|
|
* Get additionals information to add after name
|
|
*
|
|
* @since 0.84
|
|
*
|
|
* @return string string to add
|
|
**/
|
|
function getPostAdditionalInfosForName() {
|
|
return '';
|
|
}
|
|
|
|
|
|
/**
|
|
* Get the name of the object with the ID if the config is set
|
|
* Should Not be overloaded (overload getName() instead)
|
|
*
|
|
* @see CommonDBTM::getName
|
|
*
|
|
* @param array $options array of options
|
|
* - comments : boolean / display comments
|
|
* - complete : boolean / display completename instead of name
|
|
* - additional : boolean / display aditionals information
|
|
* - forceid : boolean override config and display item's ID (false by default)
|
|
*
|
|
* @return string name of the object in the current language
|
|
**/
|
|
function getNameID($options = []) {
|
|
|
|
$p = [
|
|
'forceid' => false,
|
|
'comments' => false,
|
|
];
|
|
|
|
if (is_array($options)) {
|
|
foreach ($options as $key => $val) {
|
|
$p[$key] = $val;
|
|
}
|
|
}
|
|
|
|
if ($p['forceid']
|
|
|| $_SESSION['glpiis_ids_visible']) {
|
|
$addcomment = $p['comments'];
|
|
|
|
// unset comment
|
|
$p['comments'] = false;
|
|
$name = $this->getName($p);
|
|
|
|
//TRANS: %1$s is a name, %2$s is ID
|
|
$name = sprintf(__('%1$s (%2$s)'), $name, $this->getField('id'));
|
|
|
|
if ($addcomment) {
|
|
$comment = $this->getComments();
|
|
if (!empty($comment)) {
|
|
$name = sprintf(__('%1$s - %2$s'), $name, $comment);
|
|
}
|
|
}
|
|
return $name;
|
|
}
|
|
return $this->getName($options);
|
|
}
|
|
|
|
/**
|
|
* Get the Search options for the given Type
|
|
* If you want to work on search options, @see CommonDBTM::rawSearchOptions
|
|
*
|
|
* @return array an *indexed* array of search options
|
|
*
|
|
* @see https://glpi-developer-documentation.rtfd.io/en/master/devapi/search.html
|
|
**/
|
|
public final function searchOptions() {
|
|
static $options;
|
|
|
|
if (!isset($options)) {
|
|
$options = [];
|
|
|
|
foreach ($this->rawSearchOptions() as $opt) {
|
|
$missingFields = [];
|
|
if (!isset($opt['id'])) {
|
|
$missingFields[] = 'id';
|
|
}
|
|
if (!isset($opt['name'])) {
|
|
$missingFields[] = 'name';
|
|
}
|
|
if (count($missingFields) > 0) {
|
|
throw new \Exception(
|
|
vsprintf(
|
|
'Invalid search option in "%1$s": missing "%2$s" field(s). %3$s',
|
|
[
|
|
get_called_class(),
|
|
implode('", "', $missingFields),
|
|
print_r($opt, true)
|
|
]
|
|
)
|
|
);
|
|
}
|
|
|
|
$optid = $opt['id'];
|
|
unset($opt['id']);
|
|
|
|
if (isset($options[$optid])) {
|
|
$message = "Duplicate key $optid ({$options[$optid]['name']}/{$opt['name']}) in ".
|
|
get_class($this) . " searchOptions!";
|
|
|
|
Toolbox::logError($message);
|
|
}
|
|
|
|
foreach ($opt as $k => $v) {
|
|
$options[$optid][$k] = $v;
|
|
}
|
|
}
|
|
}
|
|
|
|
return $options;
|
|
}
|
|
|
|
|
|
/**
|
|
* Provides search options configuration. Do not rely directly
|
|
* on this, @see CommonDBTM::searchOptions instead.
|
|
*
|
|
* @since 9.3
|
|
*
|
|
* This should be overloaded in Class
|
|
*
|
|
* @return array a *not indexed* array of search options
|
|
*
|
|
* @see https://glpi-developer-documentation.rtfd.io/en/master/devapi/search.html
|
|
**/
|
|
public function rawSearchOptions() {
|
|
$tab = [];
|
|
|
|
$tab[] = [
|
|
'id' => 'common',
|
|
'name' => __('Characteristics')
|
|
];
|
|
|
|
if ($this->isField('name')) {
|
|
$tab[] = [
|
|
'id' => 1,
|
|
'table' => $this->getTable(),
|
|
'field' => 'name',
|
|
'name' => __('Name'),
|
|
'datatype' => 'itemlink',
|
|
'massiveaction' => false,
|
|
'autocomplete' => true,
|
|
];
|
|
}
|
|
|
|
if ($this->isField('is_recursive')) {
|
|
$tab[] = [
|
|
'id' => 86,
|
|
'table' => $this->getTable(),
|
|
'field' => 'is_recursive',
|
|
'name' => __('Child entities'),
|
|
'datatype' => 'bool',
|
|
'searchtype' => 'equals',
|
|
];
|
|
}
|
|
|
|
// add objectlock search options
|
|
$tab = array_merge($tab, ObjectLock::rawSearchOptionsToAdd(get_class($this)));
|
|
|
|
return $tab;
|
|
}
|
|
|
|
/**
|
|
* Summary of getSearchOptionsToAdd
|
|
* @since 9.2
|
|
*
|
|
* @param string $itemtype Item type, defaults to null
|
|
*
|
|
* @return array
|
|
**/
|
|
static function getSearchOptionsToAdd($itemtype = null) {
|
|
$options = [];
|
|
|
|
$classname = get_called_class();
|
|
$method_name = 'rawSearchOptionsToAdd';
|
|
if (!method_exists($classname, $method_name)) {
|
|
return $options;
|
|
}
|
|
|
|
if (defined('TU_USER') && $itemtype != null && $itemtype != 'AllAssets') {
|
|
$item = new $itemtype;
|
|
$all_options = $item->searchOptions();
|
|
}
|
|
|
|
foreach ($classname::$method_name($itemtype) as $opt) {
|
|
if (!isset($opt['id'])) {
|
|
throw new \Exception(get_called_class() . ': invalid search option! ' . print_r($opt, true));
|
|
}
|
|
$optid = $opt['id'];
|
|
unset($opt['id']);
|
|
|
|
if (defined('TU_USER') && $itemtype != null) {
|
|
if (isset($all_options[$optid])) {
|
|
$message = "Duplicate key $optid ({$all_options[$optid]['name']}/{$opt['name']}) in ".
|
|
self::class . " searchOptionsToAdd for $itemtype!";
|
|
|
|
Toolbox::logError($message);
|
|
}
|
|
}
|
|
|
|
foreach ($opt as $k => $v) {
|
|
$options[$optid][$k] = $v;
|
|
if (defined('TU_USER') && $itemtype != null) {
|
|
$all_options[$optid][$k] = $v;
|
|
}
|
|
}
|
|
}
|
|
|
|
return $options;
|
|
}
|
|
|
|
/**
|
|
* Get all the massive actions available for the current class regarding given itemtype
|
|
*
|
|
* @since 0.85
|
|
*
|
|
* @param array $actions array of the actions to update
|
|
* @param string $itemtype the type of the item for which we want the actions
|
|
* @param boolean $is_deleted (default 0)
|
|
* @param CommonDBTM $checkitem (default NULL)
|
|
*
|
|
* @return void (update is set inside $actions)
|
|
**/
|
|
static function getMassiveActionsForItemtype(array &$actions, $itemtype, $is_deleted = 0,
|
|
CommonDBTM $checkitem = null) {
|
|
}
|
|
|
|
|
|
/**
|
|
* Class-specific method used to show the fields to specify the massive action
|
|
*
|
|
* @since 0.85
|
|
*
|
|
* @param MassiveAction $ma the current massive action object
|
|
*
|
|
* @return boolean false if parameters displayed ?
|
|
**/
|
|
static function showMassiveActionsSubForm(MassiveAction $ma) {
|
|
return false;
|
|
}
|
|
|
|
|
|
/**
|
|
* Class specific execution of the massive action (new system) by itemtypes
|
|
*
|
|
* @since 0.85
|
|
*
|
|
* @param MassiveAction $ma the current massive action object
|
|
* @param CommonDBTM $item the item on which apply the massive action
|
|
* @param array $ids an array of the ids of the item on which apply the action
|
|
*
|
|
* @return void (direct submit to $ma object)
|
|
**/
|
|
static function processMassiveActionsForOneItemtype(MassiveAction $ma, CommonDBTM $item,
|
|
array $ids) {
|
|
}
|
|
|
|
|
|
/**
|
|
* Get the standard massive actions which are forbidden
|
|
*
|
|
* @since 0.84
|
|
*
|
|
* This should be overloaded in Class
|
|
*
|
|
* @return array an array of massive actions
|
|
**/
|
|
function getForbiddenStandardMassiveAction() {
|
|
return [];
|
|
}
|
|
|
|
|
|
/**
|
|
* Get forbidden single action
|
|
*
|
|
* @since 9.5.0
|
|
*
|
|
* @return array
|
|
**/
|
|
public function getForbiddenSingleMassiveActions() {
|
|
$excluded = [
|
|
'*:update',
|
|
'*:delete',
|
|
'*:remove',
|
|
'*:purge',
|
|
'*:unlock'
|
|
];
|
|
|
|
if (Infocom::canApplyOn($this)) {
|
|
$ic = new Infocom();
|
|
if ($ic->getFromDBforDevice($this->getType(), $this->fields['id'])) {
|
|
$excluded[] = 'Infocom:activate';
|
|
}
|
|
}
|
|
|
|
return $excluded;
|
|
}
|
|
|
|
/**
|
|
* Get whitelisted single actions
|
|
*
|
|
* @since 9.5.0
|
|
*
|
|
* @return array
|
|
**/
|
|
public function getWhitelistedSingleMassiveActions() {
|
|
return ['MassiveAction:add_transfer_list'];
|
|
}
|
|
|
|
|
|
/**
|
|
* Get the specific massive actions
|
|
*
|
|
* @since 0.84
|
|
*
|
|
* This should be overloaded in Class
|
|
*
|
|
* @param object $checkitem link item to check right (default NULL)
|
|
*
|
|
* @return array an array of massive actions
|
|
**/
|
|
function getSpecificMassiveActions($checkitem = null) {
|
|
global $DB;
|
|
|
|
$actions = [];
|
|
// test if current profile has rights to unlock current item type
|
|
if (Session::haveRight( static::$rightname, UNLOCK)) {
|
|
$actions['ObjectLock'.MassiveAction::CLASS_ACTION_SEPARATOR.'unlock']
|
|
= _x('button', 'Unlock items');
|
|
}
|
|
if ($DB->fieldExists(static::getTable(), 'entities_id') && static::canUpdate()) {
|
|
MassiveAction::getAddTransferList($actions);
|
|
}
|
|
|
|
if (in_array(static::getType(), Appliance::getTypes(true)) && static::canUpdate()) {
|
|
$actions['Appliance' . MassiveAction::CLASS_ACTION_SEPARATOR . 'add_item'] = _x('button', 'Associate to an appliance');
|
|
}
|
|
|
|
return $actions;
|
|
}
|
|
|
|
|
|
/**
|
|
* Print out an HTML "<select>" for a dropdown
|
|
*
|
|
* This should be overloaded in Class
|
|
*
|
|
* @param array $options array of possible options:
|
|
* Parameters which could be used in options array :
|
|
* - name : string / name of the select (default is depending itemtype)
|
|
* - value : integer / preselected value (default 0)
|
|
* - comments : boolean / is the comments displayed near the dropdown (default true)
|
|
* - entity : integer or array / restrict to a defined entity or array of entities
|
|
* (default -1 : no restriction)
|
|
* - toupdate : array / Update a specific item on select change on dropdown
|
|
* (need value_fieldname, to_update, url (see Ajax::updateItemOnSelectEvent for information)
|
|
* and may have moreparams)
|
|
* - used : array / Already used items ID: not to display in dropdown (default empty)
|
|
*
|
|
* @return string|void display the dropdown
|
|
**/
|
|
static function dropdown($options = []) {
|
|
/// TODO try to revert usage : Dropdown::show calling this function
|
|
/// TODO use this function instead of Dropdown::show
|
|
return Dropdown::show(get_called_class(), $options);
|
|
}
|
|
|
|
|
|
/**
|
|
* Return a search option by looking for a value of a specific field and maybe a specific table
|
|
*
|
|
* @param string $field the field in which looking for the value (for example : table, name, etc)
|
|
* @param string $value the value to look for in the field
|
|
* @param string $table the table (default '')
|
|
*
|
|
* @return array the search option array, or an empty array if not found
|
|
**/
|
|
function getSearchOptionByField($field, $value, $table = '') {
|
|
|
|
foreach ($this->searchOptions() as $id => $searchOption) {
|
|
if ((isset($searchOption['linkfield']) && ($searchOption['linkfield'] == $value))
|
|
|| (isset($searchOption[$field]) && ($searchOption[$field] == $value))) {
|
|
if (($table == '')
|
|
|| (($table != '') && ($searchOption['table'] == $table))) {
|
|
// Set ID;
|
|
$searchOption['id'] = $id;
|
|
return $searchOption;
|
|
}
|
|
}
|
|
}
|
|
return [];
|
|
}
|
|
|
|
|
|
/**
|
|
* Get search options
|
|
*
|
|
* @since 0.85
|
|
*
|
|
* @return array the search option array
|
|
**/
|
|
function getOptions() {
|
|
|
|
if (!$this->searchopt) {
|
|
$this->searchopt = Search::getOptions($this->getType());
|
|
}
|
|
|
|
return $this->searchopt;
|
|
}
|
|
|
|
|
|
/**
|
|
* Return a search option ID by looking for a value of a specific field and maybe a specific table
|
|
*
|
|
* @since 0.83
|
|
*
|
|
* @param string $field the field in which looking for the value (for example : table, name, etc)
|
|
* @param string $value the value to look for in the field
|
|
* @param string $table the table (default '')
|
|
*
|
|
* @return mixed the search option id, or -1 if not found
|
|
**/
|
|
function getSearchOptionIDByField($field, $value, $table = '') {
|
|
|
|
$tab = $this->getSearchOptionByField($field, $value, $table);
|
|
if (isset($tab['id'])) {
|
|
return $tab['id'];
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
|
|
/**
|
|
* Check float and decimal values
|
|
*
|
|
* @param boolean $display display or not messages in and addAfterRedirect (true by default)
|
|
*
|
|
* @return void
|
|
**/
|
|
function filterValues($display = true) {
|
|
// MoYo : comment it because do not understand why filtering is disable
|
|
// if (in_array('CommonDBRelation', class_parents($this))) {
|
|
// return true;
|
|
// }
|
|
//Type mismatched fields
|
|
$fails = [];
|
|
if (isset($this->input) && is_array($this->input) && count($this->input)) {
|
|
|
|
foreach ($this->input as $key => $value) {
|
|
$unset = false;
|
|
$regs = [];
|
|
$searchOption = $this->getSearchOptionByField('field', $key);
|
|
|
|
if (isset($searchOption['datatype'])
|
|
&& (is_null($value) || ($value == '') || ($value == 'NULL'))) {
|
|
|
|
switch ($searchOption['datatype']) {
|
|
case 'date' :
|
|
case 'datetime' :
|
|
// don't use $unset', because this is not a failure
|
|
$this->input[$key] = 'NULL';
|
|
break;
|
|
}
|
|
} else if (isset($searchOption['datatype'])
|
|
&& !is_null($value)
|
|
&& ($value != '')
|
|
&& ($value != 'NULL')) {
|
|
|
|
switch ($searchOption['datatype']) {
|
|
case 'integer' :
|
|
case 'count' :
|
|
case 'number' :
|
|
case 'decimal' :
|
|
$value = str_replace(',', '.', $value);
|
|
if ($searchOption['datatype'] == 'decimal') {
|
|
$this->input[$key] = floatval(Toolbox::cleanDecimal($value));
|
|
} else {
|
|
$this->input[$key] = intval(Toolbox::cleanInteger($value));
|
|
}
|
|
if (!is_numeric($this->input[$key])) {
|
|
$unset = true;
|
|
}
|
|
break;
|
|
|
|
case 'bool' :
|
|
if (!in_array($value, [0,1])) {
|
|
$unset = true;
|
|
}
|
|
break;
|
|
|
|
case 'ip' :
|
|
$address = new IPAddress();
|
|
if (!$address->setAddressFromString($value)) {
|
|
$unset = true;
|
|
} else if (!$address->is_ipv4()) {
|
|
$unset = true;
|
|
}
|
|
break;
|
|
|
|
case 'mac' :
|
|
preg_match("/([0-9a-fA-F]{1,2}([:-]|$)){6}$/", $value, $regs);
|
|
if (empty($regs)) {
|
|
$unset = true;
|
|
}
|
|
// Define the MAC address to lower to reduce complexity of SQL queries
|
|
$this->input[$key] = strtolower ($value);
|
|
break;
|
|
|
|
case 'date' :
|
|
case 'datetime' :
|
|
// Date is already "reformat" according to getDateFormat()
|
|
$pattern = "/^([0-9]{4})-([0-9]{1,2})-([0-9]{1,2})";
|
|
$pattern .= "([_][01][0-9]|2[0-3]:[0-5][0-9]:[0-5]?[0-9])?/";
|
|
preg_match($pattern, $value, $regs);
|
|
if (empty($regs)) {
|
|
$unset = true;
|
|
}
|
|
break;
|
|
|
|
case 'itemtype' :
|
|
//Want to insert an itemtype, but the associated class doesn't exists
|
|
if (!class_exists($value)) {
|
|
$unset = true;
|
|
}
|
|
|
|
case 'email' :
|
|
case 'string' :
|
|
if (strlen($value) > 255) {
|
|
Toolbox::logWarning("$value exceed 255 characters long (".strlen($value)."), it will be truncated.");
|
|
$this->input[$key] = substr($value, 0, 254);
|
|
}
|
|
break;
|
|
|
|
default :
|
|
//Plugins can implement their own checks
|
|
if (!$this->checkSpecificValues($searchOption['datatype'], $value)) {
|
|
$unset = true;
|
|
}
|
|
// Copy value if check have update it
|
|
$this->input[$key] = $value;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ($unset) {
|
|
$fails[] = $searchOption['name'];
|
|
unset($this->input[$key]);
|
|
}
|
|
}
|
|
}
|
|
if ($display && count($fails)) {
|
|
//Display a message to indicate that one or more value where filtered
|
|
//TRANS: %s is the list of the failed fields
|
|
$message = sprintf(__('%1$s: %2$s'), __('At least one field has an incorrect value'),
|
|
implode(',', $fails));
|
|
Session::addMessageAfterRedirect($message, INFO, true);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Add more check for values
|
|
*
|
|
* @param string $datatype datatype of the value
|
|
* @param array $value value to check (pass by reference)
|
|
*
|
|
* @return boolean true if value is ok, false if not
|
|
**/
|
|
function checkSpecificValues($datatype, &$value) {
|
|
return true;
|
|
}
|
|
|
|
|
|
/**
|
|
* Get fields to display in the unicity error message
|
|
*
|
|
* @return array an array which contains field => label
|
|
**/
|
|
function getUnicityFieldsToDisplayInErrorMessage() {
|
|
|
|
return ['id' => __('ID'),
|
|
'serial' => __('Serial number'),
|
|
'entities_id' => Entity::getTypeName(1)];
|
|
}
|
|
|
|
|
|
function getUnallowedFieldsForUnicity() {
|
|
return ['alert', 'comment', 'date_mod', 'id', 'is_recursive', 'items_id'];
|
|
}
|
|
|
|
|
|
/**
|
|
* Build an unicity error message
|
|
*
|
|
* @param array $msgs the string not transleted to be display on the screen, or to be sent in a notification
|
|
* @param array $unicity the unicity criterion that failed to match
|
|
* @param array $doubles the items that are already present in DB
|
|
*
|
|
* @return string
|
|
**/
|
|
function getUnicityErrorMessage($msgs, $unicity, $doubles) {
|
|
|
|
$message = [];
|
|
foreach ($msgs as $field => $value) {
|
|
$table = getTableNameForForeignKeyField($field);
|
|
if ($table != '') {
|
|
$searchOption = $this->getSearchOptionByField('field', 'name', $table);
|
|
} else {
|
|
$searchOption = $this->getSearchOptionByField('field', $field);
|
|
}
|
|
$message[] = sprintf(__('%1$s = %2$s'), $searchOption['name'], $value);
|
|
}
|
|
|
|
if ($unicity['action_refuse']) {
|
|
$message_text = sprintf(__('Impossible record for %s'),
|
|
implode(' & ', $message));
|
|
} else {
|
|
$message_text = sprintf(__('Item successfully added but duplicate record on %s'),
|
|
implode(' & ', $message));
|
|
}
|
|
$message_text .= '<br>'.__('Other item exist');
|
|
|
|
foreach ($doubles as $double) {
|
|
if (in_array('CommonDBChild', class_parents($this))) {
|
|
if ($this->getField($this->itemtype)) {
|
|
$item = new $double['itemtype']();
|
|
} else {
|
|
$item = new $this->itemtype();
|
|
}
|
|
|
|
$item->getFromDB($double['items_id']);
|
|
} else {
|
|
$item = clone $this;
|
|
$item->getFromDB($double['id']);
|
|
}
|
|
|
|
$double_text = '';
|
|
if ($item->canView() && $item->canViewItem()) {
|
|
$double_text = $item->getLink();
|
|
}
|
|
|
|
foreach ($this->getUnicityFieldsToDisplayInErrorMessage() as $key => $value) {
|
|
$field_value = $item->getField($key);
|
|
if ($field_value != NOT_AVAILABLE) {
|
|
if (getTableNameForForeignKeyField($key) != '') {
|
|
$field_value = Dropdown::getDropdownName(getTableNameForForeignKeyField($key),
|
|
$field_value);
|
|
}
|
|
$new_text = sprintf(__('%1$s: %2$s'), $value, $field_value);
|
|
if (empty($double_text)) {
|
|
$double_text = $new_text;
|
|
} else {
|
|
$double_text = sprintf(__('%1$s - %2$s'), $double_text, $new_text);
|
|
}
|
|
}
|
|
}
|
|
// Add information on item in trashbin
|
|
if ($item->isField('is_deleted') && $item->getField('is_deleted')) {
|
|
$double_text = sprintf(__('%1$s - %2$s'), $double_text, __('Item in the trashbin'));
|
|
}
|
|
|
|
$message_text .= "<br>[$double_text]";
|
|
}
|
|
return $message_text;
|
|
}
|
|
|
|
|
|
/**
|
|
* Check field unicity before insert or update
|
|
*
|
|
* @param boolean $add true for insert, false for update (false by default)
|
|
* @param array $options array
|
|
*
|
|
* @return boolean true if item can be written in DB, false if not
|
|
**/
|
|
function checkUnicity($add = false, $options = []) {
|
|
global $CFG_GLPI;
|
|
|
|
$p = [
|
|
'unicity_error_message' => true,
|
|
'add_event_on_duplicate' => true,
|
|
'disable_unicity_check' => false,
|
|
];
|
|
|
|
if (is_array($options) && count($options)) {
|
|
foreach ($options as $key => $value) {
|
|
$p[$key] = $value;
|
|
}
|
|
}
|
|
|
|
// Do not check for template
|
|
if (isset($this->input['is_template']) && $this->input['is_template']) {
|
|
return true;
|
|
}
|
|
|
|
$result = true;
|
|
|
|
//Do not check unicity when creating infocoms or if checking is expliclty disabled
|
|
if ($p['disable_unicity_check']) {
|
|
return $result;
|
|
}
|
|
|
|
//Get all checks for this itemtype and this entity
|
|
if (in_array(get_class($this), $CFG_GLPI["unicity_types"])) {
|
|
// Get input entities if set / else get object one
|
|
if (isset($this->input['entities_id'])) {
|
|
$entities_id = $this->input['entities_id'];
|
|
} else if (isset($this->fields['entities_id'])) {
|
|
$entities_id = $this->fields['entities_id'];
|
|
} else {
|
|
$message = 'Missing entity ID!';
|
|
Toolbox::logError($message);
|
|
}
|
|
|
|
$all_fields = FieldUnicity::getUnicityFieldsConfig(get_class($this), $entities_id);
|
|
foreach ($all_fields as $key => $fields) {
|
|
|
|
//If there's fields to check
|
|
if (!empty($fields) && !empty($fields['fields'])) {
|
|
$where = [];
|
|
$continue = true;
|
|
foreach (explode(',', $fields['fields']) as $field) {
|
|
if (isset($this->input[$field]) //Field is set
|
|
//Standard field not null
|
|
&& (((getTableNameForForeignKeyField($field) == '')
|
|
&& ($this->input[$field] != ''))
|
|
//Foreign key and value is not 0
|
|
|| ((getTableNameForForeignKeyField($field) != '')
|
|
&& ($this->input[$field] > 0)))
|
|
&& !Fieldblacklist::isFieldBlacklisted(get_class($this), $entities_id, $field,
|
|
$this->input[$field])) {
|
|
$where[$this->getTable() . '.' . $field] = $this->input[$field];
|
|
} else {
|
|
$continue = false;
|
|
}
|
|
}
|
|
|
|
if ($continue
|
|
&& count($where)) {
|
|
$entities = $fields['entities_id'];
|
|
if ($fields['is_recursive']) {
|
|
$entities = getSonsOf('glpi_entities', $fields['entities_id']);
|
|
}
|
|
$where[] = getEntitiesRestrictCriteria($this->getTable(), '', $entities);
|
|
|
|
$tmp = clone $this;
|
|
if ($tmp->maybeTemplate()) {
|
|
$where['is_template'] = 0;
|
|
}
|
|
|
|
//If update, exclude ID of the current object
|
|
if (!$add) {
|
|
$where['NOT'] = [$this->getTable() . '.id' => $this->input['id']];
|
|
}
|
|
|
|
if (countElementsInTable($this->getTable(), $where) > 0) {
|
|
if ($p['unicity_error_message']
|
|
|| $p['add_event_on_duplicate']) {
|
|
$message = [];
|
|
foreach (explode(',', $fields['fields']) as $field) {
|
|
$message[$field] = $this->input[$field];
|
|
}
|
|
|
|
$doubles = getAllDataFromTable($this->getTable(), $where);
|
|
$message_text = $this->getUnicityErrorMessage($message, $fields, $doubles);
|
|
if ($p['unicity_error_message']) {
|
|
if (!$fields['action_refuse']) {
|
|
$show_other_messages = ($fields['action_refuse']?true:false);
|
|
} else {
|
|
$show_other_messages = true;
|
|
}
|
|
Session::addMessageAfterRedirect($message_text, true,
|
|
$show_other_messages,
|
|
$show_other_messages);
|
|
}
|
|
if ($p['add_event_on_duplicate']) {
|
|
Event::log ((!$add?$this->fields['id']:0), get_class($this), 4,
|
|
'inventory',
|
|
//TRANS: %1$s is the user login, %2$s the message
|
|
sprintf(__('%1$s trying to add an item that already exists: %2$s'),
|
|
$_SESSION["glpiname"], $message_text));
|
|
}
|
|
}
|
|
if ($fields['action_refuse']) {
|
|
$result = false;
|
|
}
|
|
if ($fields['action_notify']) {
|
|
$params = [
|
|
'action_type' => $add,
|
|
'action_user' => getUserName(Session::getLoginUserID()),
|
|
'entities_id' => $entities_id,
|
|
'itemtype' => get_class($this),
|
|
'date' => $_SESSION['glpi_currenttime'],
|
|
'refuse' => $fields['action_refuse'],
|
|
'label' => $message,
|
|
'field' => $fields,
|
|
'double' => $doubles];
|
|
NotificationEvent::raiseEvent('refuse', new FieldUnicity(), $params);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
|
|
/**
|
|
* Clean all infos which match some criteria
|
|
*
|
|
* @param array $crit array of criteria (ex array('is_active'=>'1'))
|
|
* @param boolean $force force purge not on put in trashbin (default 0)
|
|
* @param boolean $history do history log ? (true by default)
|
|
*
|
|
* @return boolean
|
|
**/
|
|
function deleteByCriteria($crit = [], $force = 0, $history = 1) {
|
|
global $DB;
|
|
|
|
$ok = false;
|
|
if (is_array($crit) && (count($crit) > 0)) {
|
|
$crit['FIELDS'] = [$this::getTable() => 'id'];
|
|
$ok = true;
|
|
$iterator = $DB->request($this->getTable(), $crit);
|
|
foreach ($iterator as $row) {
|
|
if (!$this->delete($row, $force, $history)) {
|
|
$ok = false;
|
|
}
|
|
}
|
|
|
|
}
|
|
return $ok;
|
|
}
|
|
|
|
|
|
/**
|
|
* get the Entity of an Item
|
|
*
|
|
* @param string $itemtype item type
|
|
* @param integer $items_id id of the item
|
|
*
|
|
* @return integer ID of the entity or -1
|
|
**/
|
|
static function getItemEntity($itemtype, $items_id) {
|
|
|
|
if ($itemtype
|
|
&& ($item = getItemForItemtype($itemtype))) {
|
|
|
|
if ($item->getFromDB($items_id)) {
|
|
return $item->getEntityID();
|
|
}
|
|
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
|
|
/**
|
|
* display a specific field value
|
|
*
|
|
* @since 0.83
|
|
*
|
|
* @param string $field name of the field
|
|
* @param string|array $values with the value to display or a Single value
|
|
* @param array $options Array of options
|
|
*
|
|
* @return string the string to display
|
|
**/
|
|
static function getSpecificValueToDisplay($field, $values, array $options = []) {
|
|
|
|
switch ($field) {
|
|
case '_virtual_datacenter_position':
|
|
if (method_exists(static::class, 'getDcBreadcrumbSpecificValueToDisplay')) {
|
|
return static::getDcBreadcrumbSpecificValueToDisplay($values['id']);
|
|
}
|
|
}
|
|
|
|
return '';
|
|
}
|
|
|
|
|
|
/**
|
|
* display a field using standard system
|
|
*
|
|
* @since 0.83
|
|
*
|
|
* @param integer|string|array $field_id_or_search_options id of the search option field
|
|
* or field name
|
|
* or search option array
|
|
* @param mixed $values value to display
|
|
* @param array $options array of possible options:
|
|
* Parameters which could be used in options array :
|
|
* - comments : boolean / is the comments displayed near the value (default false)
|
|
* - any others options passed to specific display method
|
|
*
|
|
* @return string the string to display
|
|
**/
|
|
function getValueToDisplay($field_id_or_search_options, $values, $options = []) {
|
|
global $CFG_GLPI;
|
|
|
|
$param = [
|
|
'comments' => false,
|
|
'html' => false,
|
|
];
|
|
foreach ($param as $key => $val) {
|
|
if (!isset($options[$key])) {
|
|
$options[$key] = $val;
|
|
}
|
|
}
|
|
|
|
$searchoptions = [];
|
|
if (is_array($field_id_or_search_options)) {
|
|
$searchoptions = $field_id_or_search_options;
|
|
} else {
|
|
$searchopt = $this->searchOptions();
|
|
|
|
// Get if id of search option is passed
|
|
if (is_numeric($field_id_or_search_options)) {
|
|
if (isset($searchopt[$field_id_or_search_options])) {
|
|
$searchoptions = $searchopt[$field_id_or_search_options];
|
|
}
|
|
} else { // Get if field name is passed
|
|
$searchoptions = $this->getSearchOptionByField('field', $field_id_or_search_options,
|
|
$this->getTable());
|
|
}
|
|
}
|
|
|
|
if (count($searchoptions)) {
|
|
$field = $searchoptions['field'];
|
|
|
|
// Normalize option
|
|
if (is_array($values)) {
|
|
$value = $values[$field];
|
|
} else {
|
|
$value = $values;
|
|
$values = [$field => $value];
|
|
}
|
|
|
|
if (isset($searchoptions['datatype'])) {
|
|
$unit = '';
|
|
if (isset($searchoptions['unit'])) {
|
|
$unit = $searchoptions['unit'];
|
|
}
|
|
|
|
switch ($searchoptions['datatype']) {
|
|
case "count" :
|
|
case "number" :
|
|
if (isset($searchoptions['toadd']) && isset($searchoptions['toadd'][$value])) {
|
|
return $searchoptions['toadd'][$value];
|
|
}
|
|
if ($options['html']) {
|
|
return Dropdown::getValueWithUnit(Html::formatNumber($value, false, 0), $unit);
|
|
}
|
|
return $value;
|
|
|
|
case "decimal" :
|
|
if ($options['html']) {
|
|
return Dropdown::getValueWithUnit(Html::formatNumber($value), $unit);
|
|
}
|
|
return $value;
|
|
|
|
case "string" :
|
|
case "mac" :
|
|
case "ip" :
|
|
return $value;
|
|
|
|
case "text" :
|
|
|
|
if ($options['html']) {
|
|
$text = nl2br($value);
|
|
} else {
|
|
$text = $value;
|
|
}
|
|
if (isset($searchoptions['htmltext']) && $searchoptions['htmltext']) {
|
|
$text = Html::clean(Toolbox::unclean_cross_side_scripting_deep($text));
|
|
}
|
|
return $text;
|
|
|
|
case "bool" :
|
|
return Dropdown::getYesNo($value);
|
|
|
|
case "date" :
|
|
case "date_delay" :
|
|
if (isset($options['relative_dates']) && $options['relative_dates']) {
|
|
$dates = Html::getGenericDateTimeSearchItems(['with_time' => true,
|
|
'with_future' => true]);
|
|
return $dates[$value];
|
|
}
|
|
return Html::convDate(Html::computeGenericDateTimeSearch($value, true));
|
|
|
|
case "datetime" :
|
|
if (isset($options['relative_dates']) && $options['relative_dates']) {
|
|
$dates = Html::getGenericDateTimeSearchItems(['with_time' => true,
|
|
'with_future' => true]);
|
|
return $dates[$value];
|
|
}
|
|
return Html::convDateTime(Html::computeGenericDateTimeSearch($value, false));
|
|
|
|
case "timestamp" :
|
|
if (($value == 0)
|
|
&& isset($searchoptions['emptylabel'])) {
|
|
return $searchoptions['emptylabel'];
|
|
}
|
|
$withseconds = false;
|
|
if (isset($searchoptions['withseconds'])) {
|
|
$withseconds = $searchoptions['withseconds'];
|
|
}
|
|
return Html::timestampToString($value, $withseconds);
|
|
|
|
case "email" :
|
|
if ($options['html']) {
|
|
return "<a href='mailto:$value'>$value</a>";
|
|
}
|
|
return $value;
|
|
|
|
case "weblink" :
|
|
$orig_link = trim($value);
|
|
if (!empty($orig_link)) {
|
|
// strip begin of link
|
|
$link = preg_replace('/https?:\/\/(www[^\.]*\.)?/', '', $orig_link);
|
|
$link = preg_replace('/\/$/', '', $link);
|
|
if (Toolbox::strlen($link) > $CFG_GLPI["url_maxlength"]) {
|
|
$link = Toolbox::substr($link, 0, $CFG_GLPI["url_maxlength"])."...";
|
|
}
|
|
return "<a href=\"".Toolbox::formatOutputWebLink($orig_link)."\" target='_blank'>$link".
|
|
"</a>";
|
|
}
|
|
return " ";
|
|
|
|
case "itemlink" :
|
|
if ($searchoptions['table'] == $this->getTable()) {
|
|
break;
|
|
}
|
|
|
|
case "dropdown" :
|
|
if (isset($searchoptions['toadd']) && isset($searchoptions['toadd'][$value])) {
|
|
return $searchoptions['toadd'][$value];
|
|
}
|
|
if (!is_numeric($value)) {
|
|
return $value;
|
|
}
|
|
|
|
if (($value == 0)
|
|
&& isset($searchoptions['emptylabel'])) {
|
|
return $searchoptions['emptylabel'];
|
|
}
|
|
|
|
if ($searchoptions['table'] == 'glpi_users') {
|
|
if ($param['comments']) {
|
|
$tmp = getUserName($value, 2);
|
|
return $tmp['name'].' '.Html::showToolTip($tmp['comment'],
|
|
['display' => false]);
|
|
}
|
|
return getUserName($value);
|
|
}
|
|
if ($param['comments']) {
|
|
$tmp = Dropdown::getDropdownName($searchoptions['table'], $value, 1);
|
|
return $tmp['name'].' '.Html::showToolTip($tmp['comment'],
|
|
['display' => false]);
|
|
}
|
|
return Dropdown::getDropdownName($searchoptions['table'], $value);
|
|
|
|
case "itemtypename" :
|
|
if ($obj = getItemForItemtype($value)) {
|
|
return $obj->getTypeName(1);
|
|
}
|
|
break;
|
|
|
|
case "language" :
|
|
if (isset($CFG_GLPI['languages'][$value])) {
|
|
return $CFG_GLPI['languages'][$value][0];
|
|
}
|
|
return __('Default value');
|
|
|
|
}
|
|
}
|
|
// Get specific display if available
|
|
$itemtype = getItemTypeForTable($searchoptions['table']);
|
|
if ($item = getItemForItemtype($itemtype)) {
|
|
$options['searchopt'] = $searchoptions;
|
|
$specific = $item->getSpecificValueToDisplay($field, $values, $options);
|
|
if (!empty($specific)) {
|
|
return $specific;
|
|
}
|
|
}
|
|
|
|
}
|
|
return $value;
|
|
}
|
|
|
|
/**
|
|
* display a specific field selection system
|
|
*
|
|
* @since 0.83
|
|
*
|
|
* @param string $field name of the field
|
|
* @param string $name name of the select (if empty use linkfield) (default '')
|
|
* @param string|array $values with the value to select or a Single value (default '')
|
|
* @param array $options aArray of options
|
|
*
|
|
* @return string the string to display
|
|
**/
|
|
static function getSpecificValueToSelect($field, $name = '', $values = '', array $options = []) {
|
|
return '';
|
|
}
|
|
|
|
|
|
/**
|
|
* Select a field using standard system
|
|
*
|
|
* @since 0.83
|
|
*
|
|
* @param integer|string|array $field_id_or_search_options id of the search option field
|
|
* or field name
|
|
* or search option array
|
|
* @param string $name name of the select (if empty use linkfield)
|
|
* (default '')
|
|
* @param mixed $values default value to display
|
|
* (default '')
|
|
* @param array $options array of possible options:
|
|
* Parameters which could be used in options array :
|
|
* - comments : boolean / is the comments displayed near the value (default false)
|
|
* - any others options passed to specific display method
|
|
*
|
|
* @return string the string to display
|
|
**/
|
|
function getValueToSelect($field_id_or_search_options, $name = '', $values = '', $options = []) {
|
|
global $CFG_GLPI;
|
|
|
|
$param = [
|
|
'comments' => false,
|
|
'html' => false,
|
|
];
|
|
foreach ($param as $key => $val) {
|
|
if (!isset($options[$key])) {
|
|
$options[$key] = $val;
|
|
}
|
|
}
|
|
|
|
$searchoptions = [];
|
|
if (is_array($field_id_or_search_options)) {
|
|
$searchoptions = $field_id_or_search_options;
|
|
} else {
|
|
$searchopt = $this->searchOptions();
|
|
|
|
// Get if id of search option is passed
|
|
if (is_numeric($field_id_or_search_options)) {
|
|
if (isset($searchopt[$field_id_or_search_options])) {
|
|
$searchoptions = $searchopt[$field_id_or_search_options];
|
|
}
|
|
} else { // Get if field name is passed
|
|
$searchoptions = $this->getSearchOptionByField('field', $field_id_or_search_options,
|
|
$this->getTable());
|
|
}
|
|
}
|
|
if (count($searchoptions)) {
|
|
$field = $searchoptions['field'];
|
|
// Normalize option
|
|
if (is_array($values)) {
|
|
$value = $values[$field];
|
|
} else {
|
|
$value = $values;
|
|
$values = [$field => $value];
|
|
}
|
|
|
|
if (empty($name)) {
|
|
$name = $searchoptions['linkfield'];
|
|
}
|
|
// If not set : set to specific
|
|
if (!isset($searchoptions['datatype'])) {
|
|
$searchoptions['datatype'] = 'specific';
|
|
}
|
|
|
|
$options['display'] = false;
|
|
|
|
if (isset($options[$searchoptions['table'].'.'.$searchoptions['field']])) {
|
|
$options = array_merge($options,
|
|
$options[$searchoptions['table'].'.'.$searchoptions['field']]);
|
|
}
|
|
|
|
switch ($searchoptions['datatype']) {
|
|
case "count" :
|
|
case "number" :
|
|
case "integer" :
|
|
$copytooption = ['min', 'max', 'step', 'toadd', 'unit'];
|
|
foreach ($copytooption as $key) {
|
|
if (isset($searchoptions[$key]) && !isset($options[$key])) {
|
|
$options[$key] = $searchoptions[$key];
|
|
}
|
|
}
|
|
$options['value'] = $value;
|
|
return Dropdown::showNumber($name, $options);
|
|
|
|
case "decimal" :
|
|
case "mac" :
|
|
case "ip" :
|
|
case "string" :
|
|
case "email" :
|
|
case "weblink" :
|
|
$this->fields[$name] = $value;
|
|
return Html::autocompletionTextField($this, $name, $options);
|
|
|
|
case "text" :
|
|
$out = '';
|
|
if (isset($searchoptions['htmltext']) && $searchoptions['htmltext']) {
|
|
$out = Html::initEditorSystem($name, '', false);
|
|
}
|
|
return $out."<textarea cols='45' rows='5' name='$name'>$value</textarea>";
|
|
|
|
case "bool" :
|
|
return Dropdown::showYesNo($name, $value, -1, $options);
|
|
|
|
case "color" :
|
|
return Html::showColorField($name, $options);
|
|
|
|
case "date" :
|
|
case "date_delay" :
|
|
if (isset($options['relative_dates']) && $options['relative_dates']) {
|
|
if (isset($searchoptions['maybefuture']) && $searchoptions['maybefuture']) {
|
|
$options['with_future'] = true;
|
|
}
|
|
return Html::showGenericDateTimeSearch($name, $value, $options);
|
|
}
|
|
$copytooption = ['min', 'max', 'maybeempty', 'showyear'];
|
|
foreach ($copytooption as $key) {
|
|
if (isset($searchoptions[$key]) && !isset($options[$key])) {
|
|
$options[$key] = $searchoptions[$key];
|
|
}
|
|
}
|
|
$options['value'] = $value;
|
|
return Html::showDateField($name, $options);
|
|
|
|
case "datetime" :
|
|
if (isset($options['relative_dates']) && $options['relative_dates']) {
|
|
if (isset($searchoptions['maybefuture']) && $searchoptions['maybefuture']) {
|
|
$options['with_future'] = true;
|
|
}
|
|
$options['with_time'] = true;
|
|
return Html::showGenericDateTimeSearch($name, $value, $options);
|
|
}
|
|
$copytooption = ['mindate', 'maxdate', 'mintime', 'maxtime',
|
|
'maybeempty', 'timestep'];
|
|
foreach ($copytooption as $key) {
|
|
if (isset($searchoptions[$key]) && !isset($options[$key])) {
|
|
$options[$key] = $searchoptions[$key];
|
|
}
|
|
}
|
|
$options['value'] = $value;
|
|
return Html::showDateTimeField($name, $options);
|
|
|
|
case "timestamp" :
|
|
$copytooption = ['addfirstminutes', 'emptylabel', 'inhours', 'max', 'min',
|
|
'step', 'toadd', 'display_emptychoice'];
|
|
foreach ($copytooption as $key) {
|
|
if (isset($searchoptions[$key]) && !isset($options[$key])) {
|
|
$options[$key] = $searchoptions[$key];
|
|
}
|
|
}
|
|
$options['value'] = $value;
|
|
return Dropdown::showTimeStamp($name, $options);
|
|
|
|
case "itemlink" :
|
|
// Do not use dropdown if wanted to select string value instead of ID
|
|
if (isset($options['itemlink_as_string']) && $options['itemlink_as_string']) {
|
|
break;
|
|
}
|
|
|
|
case "dropdown" :
|
|
$copytooption = ['condition', 'displaywith', 'emptylabel',
|
|
'right', 'toadd'];
|
|
$options['name'] = $name;
|
|
$options['value'] = $value;
|
|
foreach ($copytooption as $key) {
|
|
if (isset($searchoptions[$key]) && !isset($options[$key])) {
|
|
$options[$key] = $searchoptions[$key];
|
|
}
|
|
}
|
|
if (!isset($options['entity'])) {
|
|
$options['entity'] = $_SESSION['glpiactiveentities'];
|
|
}
|
|
$itemtype = getItemTypeForTable($searchoptions['table']);
|
|
|
|
return $itemtype::dropdown($options);
|
|
|
|
case "right" :
|
|
return Profile::dropdownRights(Profile::getRightsFor($searchoptions['rightclass']),
|
|
$name, $value, ['multiple' => false,
|
|
'display' => false]);
|
|
|
|
case "itemtypename" :
|
|
if (isset($searchoptions['itemtype_list'])) {
|
|
$options['types'] = $CFG_GLPI[$searchoptions['itemtype_list']];
|
|
}
|
|
$copytooption = ['types'];
|
|
$options['value'] = $value;
|
|
foreach ($copytooption as $key) {
|
|
if (isset($searchoptions[$key]) && !isset($options[$key])) {
|
|
$options[$key] = $searchoptions[$key];
|
|
}
|
|
}
|
|
if (isset($options['types'])) {
|
|
return Dropdown::showItemTypes($name, $options['types'],
|
|
$options);
|
|
}
|
|
return false;
|
|
|
|
case "language" :
|
|
$copytooption = ['emptylabel', 'display_emptychoice'];
|
|
foreach ($copytooption as $key) {
|
|
if (isset($searchoptions[$key]) && !isset($options[$key])) {
|
|
$options[$key] = $searchoptions[$key];
|
|
}
|
|
}
|
|
$options['value'] = $value;
|
|
return Dropdown::showLanguages($name, $options);
|
|
|
|
}
|
|
// Get specific display if available
|
|
$itemtype = getItemTypeForTable($searchoptions['table']);
|
|
if ($item = getItemForItemtype($itemtype)) {
|
|
$specific = $item->getSpecificValueToSelect($searchoptions['field'], $name,
|
|
$values, $options);
|
|
if (strlen($specific)) {
|
|
return $specific;
|
|
}
|
|
}
|
|
}
|
|
// default case field text
|
|
$this->fields[$name] = $value;
|
|
return Html::autocompletionTextField($this, $name, $options);
|
|
}
|
|
|
|
|
|
/**
|
|
* @param string $itemtype Item type
|
|
* @param string $target Target
|
|
* @param boolean $add (default 0)
|
|
*
|
|
* @return false|void
|
|
*/
|
|
static function listTemplates($itemtype, $target, $add = 0) {
|
|
global $DB;
|
|
|
|
if (!($item = getItemForItemtype($itemtype))) {
|
|
return false;
|
|
}
|
|
|
|
if (!$item->maybeTemplate()) {
|
|
return false;
|
|
}
|
|
|
|
// Avoid to get old data
|
|
$item->clearSavedInput();
|
|
|
|
//Check is user have minimum right r
|
|
if (!$item->canView()
|
|
&& !$item->canCreate()) {
|
|
return false;
|
|
}
|
|
|
|
$request = [
|
|
'FROM' => $item->getTable(),
|
|
'WHERE' => [
|
|
'is_template' => 1
|
|
],
|
|
'ORDER' => ['template_name']
|
|
];
|
|
|
|
if ($item->isEntityAssign()) {
|
|
$request['WHERE'] = $request['WHERE'] + getEntitiesRestrictCriteria(
|
|
$item->getTable(),
|
|
'entities_id',
|
|
$_SESSION['glpiactiveentities'],
|
|
$item->maybeRecursive()
|
|
);
|
|
}
|
|
|
|
if (Session::isMultiEntitiesMode()) {
|
|
$colspan=3;
|
|
} else {
|
|
$colspan=2;
|
|
}
|
|
|
|
$iterator = $DB->request($request);
|
|
$blank_params = (strpos($target, '?') ? '&' : '?') . "id=-1&withtemplate=2";
|
|
$target_blank = $target . $blank_params;
|
|
|
|
if ($add && count($iterator) == 0) {
|
|
//if there is no template, just use blank
|
|
Html::redirect($target_blank);
|
|
}
|
|
|
|
echo "<div class='center'><table class='tab_cadre'>";
|
|
if ($add) {
|
|
echo "<tr><th>" . $item->getTypeName(1)."</th>";
|
|
echo "<th>".__('Choose a template')."</th></tr>";
|
|
echo "<tr><td class='tab_bg_1 center' colspan='$colspan'>";
|
|
echo "<a href=\"" . Html::entities_deep($target_blank) . "\">".__('Blank Template')."</a></td>";
|
|
echo "</tr>";
|
|
} else {
|
|
echo "<tr><th>".$item->getTypeName(1)."</th>";
|
|
if (Session::isMultiEntitiesMode()) {
|
|
echo "<th>".Entity::getTypeName(1)."</th>";
|
|
}
|
|
echo "<th>".__('Templates')."</th></tr>";
|
|
}
|
|
|
|
while ($data = $iterator->next()) {
|
|
$templname = $data["template_name"];
|
|
if ($_SESSION["glpiis_ids_visible"] || empty($data["template_name"])) {
|
|
$templname = sprintf(__('%1$s (%2$s)'), $templname, $data["id"]);
|
|
}
|
|
if (Session::isMultiEntitiesMode()) {
|
|
$entity = Dropdown::getDropdownName('glpi_entities', $data['entities_id']);
|
|
}
|
|
if ($item->canCreate() && !$add) {
|
|
$modify_params =
|
|
(strpos($target, '?') ? '&' : '?')
|
|
. "id=".$data['id']
|
|
. "&withtemplate=1";
|
|
$target_modify = $target . $modify_params;
|
|
|
|
echo "<tr><td class='tab_bg_1 center'>";
|
|
echo "<a href=\"$target_modify\">";
|
|
echo " $templname </a></td>";
|
|
if (Session::isMultiEntitiesMode()) {
|
|
echo "<td class='tab_bg_1 center'>$entity</td>";
|
|
}
|
|
echo "<td class='tab_bg_2 center b'>";
|
|
if ($item->can($data['id'], PURGE)) {
|
|
Html::showSimpleForm($target, 'purge', _x('button', 'Delete permanently'),
|
|
['withtemplate' => 1,
|
|
'id' => $data['id']]);
|
|
}
|
|
echo "</td>";
|
|
} else {
|
|
$add_params =
|
|
(strpos($target, '?') ? '&' : '?')
|
|
. "id=".$data['id']
|
|
. "&withtemplate=2";
|
|
$target_add = $target . $add_params;
|
|
|
|
echo "<tr><td class='tab_bg_1 center' colspan='2'>";
|
|
echo "<a href=\"$target_add\">";
|
|
echo " $templname </a></td>";
|
|
}
|
|
echo "</tr>";
|
|
}
|
|
|
|
if ($item->canCreate() && !$add) {
|
|
$create_params =
|
|
(strpos($target, '?') ? '&' : '?')
|
|
. "withtemplate=1";
|
|
$target_create = $target . $create_params;
|
|
echo "<tr><td class='tab_bg_2 center b' colspan='3'>";
|
|
echo "<a href=\"$target_create\">" . __('Add a template...') . "</a>";
|
|
echo "</td></tr>";
|
|
}
|
|
echo "</table></div>\n";
|
|
}
|
|
|
|
|
|
/**
|
|
* Specificy a plugin itemtype for which entities_id and is_recursive should be forwarded
|
|
*
|
|
* @since 0.83
|
|
*
|
|
* @param string $for_itemtype change of entity for this itemtype will be forwarder
|
|
* @param string $to_itemtype change of entity will affect this itemtype
|
|
*
|
|
* @return void
|
|
**/
|
|
static function addForwardEntity($for_itemtype, $to_itemtype) {
|
|
self::$plugins_forward_entity[$for_itemtype][] = $to_itemtype;
|
|
}
|
|
|
|
|
|
/**
|
|
* Is entity informations forward To ?
|
|
*
|
|
* @since 0.84
|
|
*
|
|
* @param string $itemtype itemtype to check
|
|
*
|
|
* @return boolean
|
|
**/
|
|
static function isEntityForwardTo($itemtype) {
|
|
|
|
if (in_array($itemtype, static::$forward_entity_to)) {
|
|
return true;
|
|
}
|
|
//Fill forward_entity_to array with itemtypes coming from plugins
|
|
if (isset(static::$plugins_forward_entity[static::getType()])
|
|
&& in_array($itemtype, static::$plugins_forward_entity[static::getType()])) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
/**
|
|
* Get rights for an item _ may be overload by object
|
|
*
|
|
* @since 0.85
|
|
*
|
|
* @param string $interface (defalt 'central')
|
|
*
|
|
* @return array array of rights to display
|
|
**/
|
|
function getRights($interface = 'central') {
|
|
|
|
$values = [CREATE => __('Create'),
|
|
READ => __('Read'),
|
|
UPDATE => __('Update'),
|
|
PURGE => ['short' => __('Purge'),
|
|
'long' => _x('button', 'Delete permanently')]];
|
|
|
|
$values += ObjectLock::getRightsToAdd( get_class($this), $interface );
|
|
|
|
if ($this->maybeDeleted()) {
|
|
$values[DELETE] = ['short' => __('Delete'),
|
|
'long' => _x('button', 'Put in trashbin')];
|
|
}
|
|
if ($this->usenotepad) {
|
|
$values[READNOTE] = ['short' => __('Read notes'),
|
|
'long' => __("Read the item's notes")];
|
|
$values[UPDATENOTE] = ['short' => __('Update notes'),
|
|
'long' => __("Update the item's notes")];
|
|
}
|
|
|
|
return $values;
|
|
}
|
|
|
|
/**
|
|
* Generate link
|
|
*
|
|
* @since 9.1
|
|
*
|
|
* @param string $link original string content
|
|
* @param CommonDBTM $item item used to make replacements
|
|
*
|
|
* @return array of link contents (may have several when item have several IP / MAC cases)
|
|
**/
|
|
static function generateLinkContents($link, CommonDBTM $item) {
|
|
return Link::generateLinkContents($link, $item);
|
|
}
|
|
|
|
|
|
/**
|
|
* add files from a textarea (from $this->input['content'])
|
|
* or a file input (from $this->input['_filename']) to an CommonDBTM object
|
|
* create document if needed
|
|
* create link from document to CommonDBTM object
|
|
*
|
|
* @since 9.2
|
|
*
|
|
* @param array $input Input data
|
|
* @param array $options array with theses keys
|
|
* - force_update (default false) update the content field of the object
|
|
* - content_field (default content) the field who receive the main text
|
|
* (with images)
|
|
* - name (default filename) name of the HTML input containing files
|
|
*
|
|
* @return array the input param transformed
|
|
**/
|
|
function addFiles(array $input, $options = []) {
|
|
global $CFG_GLPI;
|
|
|
|
$default_options = [
|
|
'force_update' => false,
|
|
'content_field' => 'content',
|
|
'name' => 'filename',
|
|
];
|
|
$options = array_merge($default_options, $options);
|
|
|
|
$uploadName = '_' . $options['name'];
|
|
$tagUploadName = '_tag_' . $options['name'];
|
|
$prefixUploadName = '_prefix_' . $options['name'];
|
|
|
|
if (!isset($input[$uploadName])
|
|
|| (count($input[$uploadName]) == 0)) {
|
|
return $input;
|
|
}
|
|
$docadded = [];
|
|
$donotif = isset($input['_donotif']) ? $input['_donotif'] : 0;
|
|
$disablenotif = isset($input['_disablenotif']) ? $input['_disablenotif'] : 0;
|
|
|
|
foreach ($input[$uploadName] as $key => $file) {
|
|
$doc = new Document();
|
|
$docitem = new Document_Item();
|
|
$docID = 0;
|
|
$filename = GLPI_TMP_DIR."/".$file;
|
|
$input2 = [];
|
|
|
|
//If file tag is present
|
|
if (isset($input[$tagUploadName])
|
|
&& !empty($input[$tagUploadName][$key])) {
|
|
$input['_tag'][$key] = $input[$tagUploadName][$key];
|
|
}
|
|
|
|
//retrieve entity
|
|
$entities_id = isset($_SESSION['glpiactive_entity']) ? $_SESSION['glpiactive_entity'] : 0;
|
|
if (isset($this->fields["entities_id"])) {
|
|
$entities_id = $this->fields["entities_id"];
|
|
} else if (isset($input['entities_id'])) {
|
|
$entities_id = $input['entities_id'];
|
|
} else if (isset($input['_job']->fields['entities_id'])) {
|
|
$entities_id = $input['_job']->fields['entities_id'];
|
|
}
|
|
|
|
// Check for duplicate
|
|
if ($doc->getFromDBbyContent($entities_id, $filename)) {
|
|
if (!$doc->fields['is_blacklisted']) {
|
|
$docID = $doc->fields["id"];
|
|
}
|
|
// File already exist, we replace the tag by the existing one
|
|
if (isset($input['_tag'][$key])
|
|
&& ($docID > 0)
|
|
&& isset($input[$options['content_field']])) {
|
|
|
|
$input[$options['content_field']] = str_replace(
|
|
$input['_tag'][$key],
|
|
$doc->fields["tag"],
|
|
$input[$options['content_field']]
|
|
);
|
|
$docadded[$docID]['tag'] = $doc->fields["tag"];
|
|
}
|
|
|
|
} else {
|
|
if ($this->getType() == 'Ticket') {
|
|
//TRANS: Default document to files attached to tickets : %d is the ticket id
|
|
$input2["name"] = addslashes(sprintf(__('Document Ticket %d'), $this->getID()));
|
|
$input2["tickets_id"] = $this->getID();
|
|
}
|
|
|
|
if (isset($input['_tag'][$key])) {
|
|
// Insert image tag
|
|
$input2["tag"] = $input['_tag'][$key];
|
|
}
|
|
|
|
$input2["entities_id"] = $entities_id;
|
|
$input2["is_recursive"] = 1;
|
|
$input2["documentcategories_id"] = $CFG_GLPI["documentcategories_id_forticket"];
|
|
$input2["_only_if_upload_succeed"] = 1;
|
|
$input2["_filename"] = [$file];
|
|
if (isset($this->input[$prefixUploadName][$key])) {
|
|
$input2[$prefixUploadName] = [$this->input[$prefixUploadName][$key]];
|
|
}
|
|
$docID = $doc->add($input2);
|
|
|
|
if (isset($input['_tag'][$key])) {
|
|
// Store image tag
|
|
$docadded[$docID]['tag'] = $doc->fields["tag"];
|
|
}
|
|
}
|
|
|
|
if ($docID > 0) {
|
|
// complete doc information
|
|
$docadded[$docID]['data'] = sprintf(__('%1$s - %2$s'),
|
|
stripslashes($doc->fields["name"]),
|
|
stripslashes($doc->fields["filename"]));
|
|
$docadded[$docID]['filepath'] = $doc->fields["filepath"];
|
|
|
|
// add doc - item link
|
|
$toadd = [
|
|
'documents_id' => $docID,
|
|
'_do_notif' => $donotif,
|
|
'_disablenotif' => $disablenotif,
|
|
'itemtype' => $this->getType(),
|
|
'items_id' => $this->getID()
|
|
];
|
|
if (isset($input['users_id'])) {
|
|
$toadd['users_id'] = $input['users_id'];
|
|
}
|
|
if (isset($input[$options['content_field']])
|
|
&& strpos($input[$options['content_field']], $doc->fields["tag"]) !== false
|
|
&& strpos($doc->fields['mime'], 'image/') !== false) {
|
|
//do not display inline docs in timeline
|
|
$toadd['timeline_position'] = CommonITILObject::NO_TIMELINE;
|
|
}
|
|
|
|
$docitem->add($toadd);
|
|
}
|
|
// Only notification for the first New doc
|
|
$donotif = false;
|
|
}
|
|
|
|
// manage content transformation
|
|
if (isset($input[$options['content_field']])) {
|
|
$input[$options['content_field']] = Toolbox::convertTagToImage(
|
|
$input[$options['content_field']],
|
|
$this,
|
|
$docadded
|
|
);
|
|
|
|
if (isset($this->input['_forcenotif'])) {
|
|
$input['_forcenotif'] = $this->input['_forcenotif'];
|
|
unset($input['_disablenotif']);
|
|
}
|
|
|
|
// force update of content on add process (we are in post_addItem function)
|
|
if ($options['force_update']) {
|
|
$this->fields[$options['content_field']] = $input[$options['content_field']];
|
|
$this->updateInDB([$options['content_field']]);
|
|
}
|
|
}
|
|
|
|
return $input;
|
|
}
|
|
|
|
/**
|
|
* Get autofill mark for/from templates
|
|
*
|
|
* @param string $field Field name
|
|
* @param array $options Withtemplate parameter
|
|
* @param string $value Optional value (if field to check is not part of current itemtype)
|
|
*
|
|
* @return string
|
|
*/
|
|
public function getAutofillMark($field, $options, $value = null) {
|
|
$mark = '';
|
|
$title = null;
|
|
if (($this->isTemplate() || $this->isNewItem()) && $options['withtemplate'] == 1) {
|
|
$title = __s('You can define an autofill template');
|
|
} else if ($this->isTemplate()) {
|
|
if ($value === null) {
|
|
$value = $this->getField($field);
|
|
}
|
|
$len = Toolbox::strlen($value);
|
|
if ($len > 8
|
|
&& Toolbox::substr($value, 0, 4) === '<'
|
|
&& Toolbox::substr($value, $len -4, 4) === '>'
|
|
&& preg_match("/\\#{1,10}/", Toolbox::substr($value, 4, $len - 8))
|
|
) {
|
|
$title = __s('Autofilled from template');
|
|
} else {
|
|
return '';
|
|
}
|
|
}
|
|
if ($title !== null) {
|
|
$mark = "<i class='fa fa-magic' title='$title'></i>";
|
|
}
|
|
return $mark;
|
|
}
|
|
|
|
/**
|
|
* Manage business rules for assets
|
|
*
|
|
* @since 9.4
|
|
*
|
|
* @param boolean $condition the condition (RuleAsset::ONADD or RuleAsset::ONUPDATE)
|
|
*
|
|
* @return void
|
|
*/
|
|
private function assetBusinessRules($condition) {
|
|
global $CFG_GLPI;
|
|
|
|
//Only process itemtype that are assets
|
|
if (in_array($this->getType(), $CFG_GLPI['asset_types'])) {
|
|
$ruleasset = new RuleAssetCollection();
|
|
$input = $this->input;
|
|
$input['_itemtype'] = $this->getType();
|
|
|
|
//If _auto is not defined : it's a manual process : set it's value to 0
|
|
if (!isset($this->input['_auto'])) {
|
|
$input['_auto'] = 0;
|
|
}
|
|
//Set the condition (add or update)
|
|
$params = [
|
|
'condition' => $condition
|
|
];
|
|
$output = $ruleasset->processAllRules($input, [], $params);
|
|
//If at least one rule has matched
|
|
if (isset($output['_rule_process'])) {
|
|
foreach ($output as $key => $value) {
|
|
if ($key == '_rule_process' || $key == '_no_rule_matches') {
|
|
continue;
|
|
}
|
|
//Add the rule output to the input array
|
|
$this->input[$key] = $value;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Ensure the relation would not create a circular parent-child relation.
|
|
* @since 9.5.0
|
|
* @param int $items_id The ID of the item to evaluate.
|
|
* @param int $parents_id The wanted parent of the specified item.
|
|
* @return bool True if there is a circular relation.
|
|
*/
|
|
static function checkCircularRelation($items_id, $parents_id) {
|
|
global $DB;
|
|
|
|
$fk = static::getForeignKeyField();
|
|
if ($items_id == 0 || $parents_id == 0 || !$DB->fieldExists(static::getTable(), $fk)) {
|
|
return false;
|
|
}
|
|
|
|
$next_parent = $parents_id;
|
|
while ($next_parent > 0) {
|
|
if ($next_parent == $items_id) {
|
|
// This item is a parent higher up
|
|
return true;
|
|
}
|
|
$iterator = $DB->request([
|
|
'SELECT' => [$fk],
|
|
'FROM' => static::getTable(),
|
|
'WHERE' => ['id' => $next_parent]
|
|
]);
|
|
if ($iterator->count()) {
|
|
$next_parent = $iterator->next()[$fk];
|
|
} else {
|
|
// Invalid parent
|
|
return false;
|
|
}
|
|
}
|
|
// No circular relations
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Get incidents, request, changes and problem linked to this object
|
|
*
|
|
* @return array
|
|
*/
|
|
public function getITILTickets(bool $count = false) {
|
|
$ticket = new Ticket();
|
|
$problem = new Problem();
|
|
$change = new Change();
|
|
|
|
$data = [
|
|
'incidents' => iterator_to_array(
|
|
$ticket->getActiveTicketsForItem(
|
|
get_class($this),
|
|
$this->getID(),
|
|
Ticket::INCIDENT_TYPE
|
|
),
|
|
false
|
|
),
|
|
'requests' => iterator_to_array(
|
|
$ticket->getActiveTicketsForItem(
|
|
get_class($this),
|
|
$this->getID(),
|
|
Ticket::DEMAND_TYPE
|
|
),
|
|
false
|
|
),
|
|
'changes' => iterator_to_array(
|
|
$change->getActiveChangesForItem(
|
|
get_class($this),
|
|
$this->getID()
|
|
),
|
|
false
|
|
),
|
|
'problems' => iterator_to_array(
|
|
$problem->getActiveProblemsForItem(
|
|
get_class($this),
|
|
$this->getID()
|
|
),
|
|
false
|
|
)
|
|
];
|
|
|
|
if ($count) {
|
|
$data['count'] = count($data['incidents'])
|
|
+ count($data['requests'])
|
|
+ count($data['changes'])
|
|
+ count($data['problems']);
|
|
}
|
|
|
|
return $data;
|
|
}
|
|
|
|
static function getIcon() {
|
|
return "fas fa-empty-icon";
|
|
}
|
|
|
|
/**
|
|
* Get cache key containing raw name for a given itemtype and id
|
|
*
|
|
* @since 9.5
|
|
*
|
|
* @param string $itemtype
|
|
* @param int $id
|
|
*/
|
|
public static function getCacheKeyForFriendlyName($itemtype, $id) {
|
|
return "raw_name__{$itemtype}__{$id}";
|
|
}
|
|
|
|
/**
|
|
* Get friendly name by items id
|
|
* The purpose of this function is to try to access the friendly name
|
|
* without having to read the object from the database
|
|
*
|
|
* @since 9.5
|
|
*
|
|
* @param int $id
|
|
*
|
|
* @return string Friendly name of the object
|
|
*/
|
|
public static function getFriendlyNameById($id) {
|
|
$item = new static();
|
|
$item->getFromDB($id);
|
|
return $item->getFriendlyName();
|
|
}
|
|
|
|
/**
|
|
* Return the computed friendly name and set the cache.
|
|
*
|
|
* @since 9.5
|
|
*
|
|
* @return string
|
|
*/
|
|
final public function getFriendlyName() {
|
|
return $this->computeFriendlyName();
|
|
}
|
|
|
|
/**
|
|
* Compute the friendly name of the object
|
|
*
|
|
* @since 9.5
|
|
*
|
|
* @return string
|
|
*/
|
|
protected function computeFriendlyName() {
|
|
if (isset($this->fields[static::getNameField()])) {
|
|
return $this->fields[static::getNameField()];
|
|
}
|
|
return '';
|
|
}
|
|
|
|
/**
|
|
* Retrieve an item from the database
|
|
*
|
|
* @param integer $ID ID of the item to get
|
|
*
|
|
* @return boolean true if succeed else false
|
|
*/
|
|
public static function getById(int $id) {
|
|
$item = new static();
|
|
|
|
if (!$item->getFromDB($id)) {
|
|
return false;
|
|
}
|
|
|
|
return $item;
|
|
}
|
|
|
|
/**
|
|
* Correct entity id if needed when cloning a template
|
|
*
|
|
* @param array $data
|
|
* @param string $parent_field
|
|
*
|
|
* @return array
|
|
*/
|
|
public static function checkTemplateEntity(
|
|
array $data,
|
|
$parent_id,
|
|
$parent_itemtype
|
|
) {
|
|
// No entity field -> no modification needed
|
|
if (!isset($data['entities_id'])) {
|
|
return $data;
|
|
}
|
|
|
|
// If the entity used in the template in not allowed for our current user,
|
|
// fallback to the parent template entity
|
|
if (!Session::haveAccessToEntity($data['entities_id'])) {
|
|
// Load parent
|
|
$parent = new $parent_itemtype();
|
|
|
|
if (!$parent->getFromDB($parent_id)) {
|
|
// Can't load parent -> no modification
|
|
return $data;
|
|
}
|
|
|
|
$data['entities_id'] = $parent->getEntityID();
|
|
}
|
|
|
|
return $data;
|
|
}
|
|
|
|
/**
|
|
* Friendly names may uses multiple fields (e.g user: first name + last name)
|
|
* Return the computed criteria to use in a WHERE clause.
|
|
*
|
|
* @param string $filter
|
|
* @return array
|
|
*/
|
|
public static function getFriendlyNameSearchCriteria(string $filter): array {
|
|
$table = static::getTable();
|
|
$name_field = static::getNameField();
|
|
$name = DBmysql::quoteName("$table.$name_field");
|
|
$filter = strtolower($filter);
|
|
|
|
return [
|
|
'RAW' => [
|
|
"LOWER($name)" => ['LIKE', "%$filter%"],
|
|
]
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Friendly names may uses multiple fields (e.g user: first name + last name)
|
|
* Return the computed field name to use in a SELECT clause.
|
|
*
|
|
* @param string $alias
|
|
* @return mixed
|
|
*/
|
|
public static function getFriendlyNameFields(string $alias = "name") {
|
|
$table = static::getTable();
|
|
$name_field = static::getNameField();
|
|
|
|
return "$table.$name_field AS $alias";
|
|
}
|
|
}
|